From c7290b0ead58c9be910555c6cef0c6419464a137 Mon Sep 17 00:00:00 2001 From: mr Date: Wed, 18 Jun 2025 08:26:10 +0200 Subject: [PATCH] draft... metrics by booking... --- conf/config.go | 2 +- controllers/admiralty.go | 198 +++++++++++++++++------------------ controllers/booking.go | 47 +++++++-- go.mod | 36 ++++--- go.sum | 89 ++++++++++------ infrastructure/interface.go | 16 ++- infrastructure/kubernetes.go | 158 +++++++++++++++------------- infrastructure/prometheus.go | 176 +++++++++++++++++++++++++++++++ 8 files changed, 489 insertions(+), 233 deletions(-) create mode 100644 infrastructure/prometheus.go diff --git a/conf/config.go b/conf/config.go index 5e48d0b..cb4cf0f 100644 --- a/conf/config.go +++ b/conf/config.go @@ -19,4 +19,4 @@ func GetConfig() *Config { instance = &Config{} }) return instance -} \ No newline at end of file +} diff --git a/controllers/admiralty.go b/controllers/admiralty.go index cc8191f..9fa6bd3 100644 --- a/controllers/admiralty.go +++ b/controllers/admiralty.go @@ -20,53 +20,50 @@ import ( ) type KubeInfo struct { - Url *string - KubeCA *string - KubeCert *string - KubeKey *string + Url *string + KubeCA *string + KubeCert *string + KubeKey *string } type RemoteKubeconfig struct { - Data *string + Data *string } type KubeUser struct { - Name string - User struct { - Token string + 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"` + 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"` + Token string `yaml:"token"` + } `yaml:"user"` + } `yaml:"users"` } - - // Operations about the admiralty objects of the datacenter type AdmiraltyController struct { beego.Controller @@ -74,19 +71,19 @@ type AdmiraltyController struct { // @Title GetAllTargets // @Description find all Admiralty Target -// @Success 200 +// @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) + 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() @@ -95,7 +92,7 @@ func (c *AdmiraltyController) GetAllTargets() { // @Title GetOneTarget // @Description find one Admiralty Target // @Param id path string true "the name of the target to get" -// @Success 200 +// @Success 200 // @router /targets/:execution [get] func (c *AdmiraltyController) GetOneTarget() { id := c.Ctx.Input.Param(":execution") @@ -107,10 +104,10 @@ func (c *AdmiraltyController) GetOneTarget() { c.Data["json"] = map[string]string{"error": err.Error()} return } - + res, err := serv.GetTargets(c.Ctx.Request.Context()) - id = "target-"+id - found := slices.Contains(res,id) + id = "target-" + id + found := slices.Contains(res, id) if !found { c.Ctx.Output.SetStatus(404) c.ServeJSON() @@ -123,7 +120,7 @@ func (c *AdmiraltyController) GetOneTarget() { // @Title CreateAdmiraltySource // @Description Create an Admiralty Source on remote cluster // @Param execution path string true "execution id of the workflow" -// @Success 201 +// @Success 201 // @router /source/:execution [post] func (c *AdmiraltyController) CreateAdmiraltySource() { @@ -140,11 +137,11 @@ func (c *AdmiraltyController) CreateAdmiraltySource() { return } - res, err := serv.CreateAdmiraltySource(c.Ctx.Request.Context(),execution) + 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.Data["json"] = map[string]string{"info": "A source already exists for this namespace : " + execution} c.ServeJSON() return } @@ -154,15 +151,15 @@ func (c *AdmiraltyController) CreateAdmiraltySource() { c.ServeJSON() return } - + // TODO : Return a description of the created resource var respData map[string]interface{} - err = json.Unmarshal(res,&respData) - + err = json.Unmarshal(res, &respData) + c.Ctx.Output.SetStatus(201) c.Data["json"] = respData c.ServeJSON() - + } // @Title CreateAdmiraltyTarget @@ -171,15 +168,15 @@ func (c *AdmiraltyController) CreateAdmiraltySource() { // @Param peer path string true "peerId of the peer the target points to" // @Success 201 // @router /target/:execution/:peer [post] -func (c *AdmiraltyController) CreateAdmiraltyTarget(){ +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.Data["json"] = map[string]string{"error": "parameters can be empty " + "execution: " + execution + " peer: " + peerId} c.ServeJSON() return } @@ -193,7 +190,7 @@ func (c *AdmiraltyController) CreateAdmiraltyTarget(){ return } - resp, err := serv.CreateAdmiraltyTarget(c.Ctx.Request.Context(),execution, peerId) + resp, err := serv.CreateAdmiraltyTarget(c.Ctx.Request.Context(), execution, peerId) if err != nil { // change code to 500 c.Ctx.Output.SetStatus(500) @@ -206,12 +203,12 @@ func (c *AdmiraltyController) CreateAdmiraltyTarget(){ fmt.Println(resp) fmt.Println(err) c.Ctx.Output.SetStatus(401) - c.Data["json"] = map[string]string{"error" : "Could not perform the action" } + c.Data["json"] = map[string]string{"error": "Could not perform the action"} c.ServeJSON() return } - err = json.Unmarshal(resp,&data) + err = json.Unmarshal(resp, &data) if err != nil { // change code to 500 c.Ctx.Output.SetStatus(500) @@ -229,15 +226,14 @@ func (c *AdmiraltyController) CreateAdmiraltyTarget(){ // @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 +// @Success 200 // @router /secret/:execution/:peer [get] -func(c *AdmiraltyController) GetKubeSecret() { - var data map[string]interface{} - +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 @@ -247,7 +243,7 @@ func(c *AdmiraltyController) GetKubeSecret() { return } - resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(),execution, peerId) + resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(), execution, peerId) if err != nil { // change code to 500 c.Ctx.Output.SetStatus(500) @@ -260,8 +256,8 @@ func(c *AdmiraltyController) GetKubeSecret() { c.ServeJSON() return } - - err = json.Unmarshal(resp,&data) + + err = json.Unmarshal(resp, &data) if err != nil { // change code to 500 c.Ctx.Output.SetStatus(500) @@ -274,18 +270,17 @@ func(c *AdmiraltyController) GetKubeSecret() { 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 +// @Success 201 // @router /secret/:execution/:peer [post] func (c *AdmiraltyController) CreateKubeSecret() { - var kubeconfig RemoteKubeconfig - var respData map[string]interface{} + var kubeconfig RemoteKubeconfig + var respData map[string]interface{} data := c.Ctx.Input.CopyBody(100000) @@ -311,7 +306,7 @@ func (c *AdmiraltyController) CreateKubeSecret() { return } - resp, err := serv.CreateKubeconfigSecret(c.Ctx.Request.Context(),*kubeconfig.Data,execution, peerId) + resp, err := serv.CreateKubeconfigSecret(c.Ctx.Request.Context(), *kubeconfig.Data, execution, peerId) if err != nil { // change code to 500 c.Ctx.Output.SetStatus(500) @@ -320,7 +315,7 @@ func (c *AdmiraltyController) CreateKubeSecret() { return } - err = json.Unmarshal(resp,&respData) + err = json.Unmarshal(resp, &respData) c.Ctx.Output.SetStatus(201) c.Data["json"] = respData c.ServeJSON() @@ -331,9 +326,9 @@ func (c *AdmiraltyController) CreateKubeSecret() { // @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 +// @Success 200 // @router /node/:execution/:peer [get] -func (c *AdmiraltyController) GetNodeReady(){ +func (c *AdmiraltyController) GetNodeReady() { var secret v1.Secret execution := c.Ctx.Input.Param(":execution") peerId := c.Ctx.Input.Param(":peer") @@ -347,7 +342,7 @@ func (c *AdmiraltyController) GetNodeReady(){ return } - node, err := serv.GetOneNode(c.Ctx.Request.Context(),execution, peerId) + node, err := serv.GetOneNode(c.Ctx.Request.Context(), execution, peerId) if err != nil { // change code to 500 c.Ctx.Output.SetStatus(500) @@ -358,15 +353,13 @@ func (c *AdmiraltyController) GetNodeReady(){ 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", + "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) + resp, err := serv.GetKubeconfigSecret(c.Ctx.Request.Context(), execution, peerId) if err != nil { // change code to 500 c.Ctx.Output.SetStatus(500) @@ -383,26 +376,26 @@ func (c *AdmiraltyController) GetNodeReady(){ // Extract JWT token RS265 encoded var editedKubeconfig map[string]interface{} - json.Unmarshal(resp,&secret) + json.Unmarshal(resp, &secret) byteEditedKubeconfig := secret.Data["config"] - err = yaml.Unmarshal(byteEditedKubeconfig,&editedKubeconfig) + 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("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) + HandleControllerErrors(c.Controller, 500, &err, nil) } - + // Decode token isExpired, err := isTokenExpired(token) if err != nil { @@ -414,19 +407,19 @@ func (c *AdmiraltyController) GetNodeReady(){ if *isExpired { c.Data["json"] = map[string]interface{}{ - "token" : "token in the secret is expired and must be regenerated", - "node": node, + "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.Data["json"] = map[string]interface{}{"node": node, "token": true} c.ServeJSON() - + } -func retrieveTokenFromKonfig(editedKubeconfig map[string]interface{}) (string,error) { +func retrieveTokenFromKonfig(editedKubeconfig map[string]interface{}) (string, error) { var kubeUsers []KubeUser b, err := yaml.Marshal(editedKubeconfig["users"]) if err != nil { @@ -434,7 +427,7 @@ func retrieveTokenFromKonfig(editedKubeconfig map[string]interface{}) (string,er fmt.Println(err) return "", err } - err = yaml.Unmarshal(b,&kubeUsers) + err = yaml.Unmarshal(b, &kubeUsers) if err != nil { fmt.Println("Error while unmarshalling users attribute from kubeconfig") fmt.Println(err) @@ -446,7 +439,7 @@ func retrieveTokenFromKonfig(editedKubeconfig map[string]interface{}) (string,er return token, nil } -func isTokenExpired(token string) (*bool, error){ +func isTokenExpired(token string) (*bool, error) { logger := oclib.GetLogger() t, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{}) @@ -465,22 +458,20 @@ func isTokenExpired(token string) (*bool, error){ 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 +// @Success 200 // @router /kubeconfig/:execution [get] func (c *AdmiraltyController) GetAdmiraltyKubeconfig() { - - execution := c.Ctx.Input.Param(":execution") + execution := c.Ctx.Input.Param(":execution") serv, err := infrastructure.NewService() if err != nil { @@ -491,7 +482,7 @@ func (c *AdmiraltyController) GetAdmiraltyKubeconfig() { return } - generatedToken, err := serv.GenerateToken(c.Ctx.Request.Context(),execution,3600) + 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) @@ -523,12 +514,11 @@ func (c *AdmiraltyController) GetAdmiraltyKubeconfig() { c.Data["json"] = map[string]string{ "data": encodedKubeconfig, } - + json.NewEncoder(c.Ctx.ResponseWriter) c.ServeJSON() } - -func NewHostKubeWithToken(token string) (*models.KubeConfigValue, error){ +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") } @@ -536,15 +526,15 @@ func NewHostKubeWithToken(token string) (*models.KubeConfigValue, error){ encodedCA := base64.StdEncoding.EncodeToString([]byte(conf.GetConfig().KubeCA)) hostKube := models.KubeConfigValue{ - APIVersion: "v1", + APIVersion: "v1", CurrentContext: "default", - Kind: "Config", - Preferences: struct{}{}, + Kind: "Config", + Preferences: struct{}{}, Clusters: []models.KubeconfigNamedCluster{ { Name: "default", Cluster: models.KubeconfigCluster{ - Server: "https://" + conf.GetConfig().KubeHost + ":6443", + Server: "https://" + conf.GetConfig().KubeHost + ":6443", CertificateAuthorityData: encodedCA, }, }, @@ -554,12 +544,12 @@ func NewHostKubeWithToken(token string) (*models.KubeConfigValue, error){ Name: "default", Context: models.KubeconfigContext{ Cluster: "default", - User: "default", + User: "default", }, }, }, Users: []models.KubeconfigUser{ - models.KubeconfigUser{ + { Name: "default", User: models.KubeconfigUserKeyPair{ Token: token, @@ -569,4 +559,4 @@ func NewHostKubeWithToken(token string) (*models.KubeConfigValue, error){ } return &hostKube, nil -} \ No newline at end of file +} diff --git a/controllers/booking.go b/controllers/booking.go index 3e490d0..f3c89ad 100644 --- a/controllers/booking.go +++ b/controllers/booking.go @@ -11,6 +11,7 @@ import ( "cloud.o-forge.io/core/oc-lib/dbs" "cloud.o-forge.io/core/oc-lib/models/booking" b "cloud.o-forge.io/core/oc-lib/models/booking" + "cloud.o-forge.io/core/oc-lib/tools" beego "github.com/beego/beego/v2/server/web" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -212,17 +213,51 @@ func (o *BookingController) Post() { user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) err := json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &resp) if err != nil { - fmt.Println("Error unmarshalling") - fmt.Println(err) - fmt.Println(resp) + 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 + } } - - 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: dc_id}}, + "resource_id": {{Operator: dbs.EQUAL.String(), Value: resp.ResourceID}}, }}, "", isDraft == "true") if res.Code != 200 { o.Data["json"] = map[string]interface{}{ diff --git a/go.mod b/go.mod index e4b5cf1..f0f2801 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 toolchain go1.23.3 require ( - cloud.o-forge.io/core/oc-lib v0.0.0-20250219142942-5111c9c8bec7 + cloud.o-forge.io/core/oc-lib v0.0.0-20250618055840-29bc21735d6e github.com/beego/beego/v2 v2.3.1 github.com/golang-jwt/jwt/v5 v5.2.2 go.mongodb.org/mongo-driver v1.17.1 @@ -15,6 +15,15 @@ require ( k8s.io/client-go v0.32.1 ) +require ( + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect +) + require ( github.com/beorn7/perks v1.0.1 // indirect github.com/biter777/countries v1.7.5 // indirect @@ -34,14 +43,14 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-cmp v0.7.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.17.11 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -55,28 +64,29 @@ require ( github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/robfig/cron v1.2.0 // indirect github.com/rs/zerolog v1.33.0 // indirect github.com/shiena/ansicolor v0.0.0-20230509054315-a9deabde6e02 // indirect + github.com/shirou/gopsutil/v3 v3.24.5 github.com/smartystreets/goconvey v1.7.2 // 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 - 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/crypto v0.31.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect - google.golang.org/protobuf v1.35.1 // indirect + google.golang.org/protobuf v1.36.5 // 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 diff --git a/go.sum b/go.sum index 78e7470..75a40a0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,17 @@ -cloud.o-forge.io/core/oc-lib v0.0.0-20250219142942-5111c9c8bec7 h1:fh6SzBPenzIxufIIzExtx4jEE4OhFposqn3EbHFr92Q= -cloud.o-forge.io/core/oc-lib v0.0.0-20250219142942-5111c9c8bec7/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE= +cloud.o-forge.io/core/oc-lib v0.0.0-20250217072519-cafadec1469f h1:esLB0EAn8IuOChW35kcBrPaN80z4A4yYyz1mXT45GQo= +cloud.o-forge.io/core/oc-lib v0.0.0-20250217072519-cafadec1469f/go.mod h1:2roQbUpv3a6mTIr5oU1ux31WbN8YucyyQvCQ0FqwbcE= +cloud.o-forge.io/core/oc-lib v0.0.0-20250616114832-9fe72ea96ef0 h1:dKT8pcy/GqqBNpU7oldAGcFM2tr9xz/OLw7cqcYZgLY= +cloud.o-forge.io/core/oc-lib v0.0.0-20250616114832-9fe72ea96ef0/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617130633-8f2adb76e41c h1:k2y+ocElqwUK5yzyCf3rWrDUzPWbds4MbtG58+Szos0= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617130633-8f2adb76e41c/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617133502-9e5266326157 h1:853UvpMOM1QuWLrr/V8biDS8IcQcqHvoJsOT4epxDng= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617133502-9e5266326157/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617141444-0b0952b28c7e h1:Z5vLv+Wzzz58abmHRnovoqbkVlKHuC8u8/RLv7FjtZw= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617141444-0b0952b28c7e/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617144221-ec7a7e474637 h1:YiZbn6KmjgZ62uM+kH95Snd2nQliDKDnGMAxRr/VoUw= +cloud.o-forge.io/core/oc-lib v0.0.0-20250617144221-ec7a7e474637/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= +cloud.o-forge.io/core/oc-lib v0.0.0-20250618055840-29bc21735d6e h1:8DAz9DXRAAbvhw1FWTvfRGKMMmnVpQBOSggKLxr25yg= +cloud.o-forge.io/core/oc-lib v0.0.0-20250618055840-29bc21735d6e/go.mod h1:vHWauJsS6ryf7UDqq8hRXoYD5RsONxcFTxeZPOztEuI= 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= @@ -29,6 +41,8 @@ github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= 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 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 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= @@ -50,8 +64,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ 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/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -60,9 +72,10 @@ 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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -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/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -86,8 +99,8 @@ 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.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.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= @@ -99,6 +112,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 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-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 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= @@ -139,12 +154,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/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.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/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= 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= @@ -156,6 +173,8 @@ 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/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/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= 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= @@ -172,8 +191,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV 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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 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= @@ -187,6 +210,8 @@ 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= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -194,8 +219,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 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= @@ -206,39 +231,43 @@ 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.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/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.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-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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.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.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 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= @@ -253,8 +282,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/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= diff --git a/infrastructure/interface.go b/infrastructure/interface.go index b51a368..469060f 100644 --- a/infrastructure/interface.go +++ b/infrastructure/interface.go @@ -15,18 +15,27 @@ type Infrastructure interface { 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) + 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) + 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) + CreateAdmiraltyTarget(context context.Context, executionId string, peerId string) ([]byte, error) GetOneNode(context context.Context, executionID string, peerId string) (*v1.Node, 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 { @@ -34,4 +43,3 @@ func NewService() (Infrastructure, error) { } return service() } - diff --git a/infrastructure/kubernetes.go b/infrastructure/kubernetes.go index e8bca8e..71bea92 100644 --- a/infrastructure/kubernetes.go +++ b/infrastructure/kubernetes.go @@ -8,6 +8,7 @@ import ( "fmt" "oc-datacenter/conf" "strings" + "time" authv1 "k8s.io/api/authentication/v1" v1 "k8s.io/api/core/v1" @@ -26,7 +27,7 @@ var gvrSources = schema.GroupVersionResource{Group: "multicluster.admiralty.io", var gvrTargets = schema.GroupVersionResource{Group: "multicluster.admiralty.io", Version: "v1alpha1", Resource: "targets"} type KubernetesService struct { - Set *kubernetes.Clientset + Set *kubernetes.Clientset } func NewDynamicClient() (*dynamic.DynamicClient, error) { @@ -59,7 +60,7 @@ func NewKubernetesService() (Infrastructure, error) { KeyData: []byte(conf.GetConfig().KubeData), }, } - + // Create clientset clientset, err := kubernetes.NewForConfig(config) fmt.Println("NewForConfig", clientset, err) @@ -70,7 +71,6 @@ func NewKubernetesService() (Infrastructure, error) { return nil, errors.New("Error creating Kubernetes client: clientset is nil") } - return &KubernetesService{ Set: clientset, }, nil @@ -111,7 +111,7 @@ func (k *KubernetesService) CreateNamespace(ctx context.Context, ns string) erro ObjectMeta: metav1.ObjectMeta{ Name: ns, Labels: map[string]string{ - "multicluster-scheduler":"enabled", + "multicluster-scheduler": "enabled", }, }, } @@ -291,24 +291,24 @@ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context, execu 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) + + targetName := "target-" + getConcatenatedName(peerId, executionId) target := map[string]interface{}{ - "apiVersion": "multicluster.admiralty.io/v1alpha1", - "kind": "Target", - "metadata": map[string]interface{}{ - "name": targetName, - "namespace": executionId, + "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), + }, + "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 { @@ -327,22 +327,21 @@ func (k *KubernetesService) CreateAdmiraltyTarget(context context.Context, execu // 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) { +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, - }, - } + "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) + 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()) } @@ -361,14 +360,12 @@ func (k *KubernetesService) CreateKubeconfigSecret(context context.Context, kube return nil, err } - secretApplyConfig := apply.Secret("kube-secret-" + getConcatenatedName(peerId, executionId), - executionId). - WithData(map[string][]byte{ - "config": config, - }, - ) - - + secretApplyConfig := apply.Secret("kube-secret-"+getConcatenatedName(peerId, executionId), + executionId). + WithData(map[string][]byte{ + "config": config, + }, + ) // exists, err := k.GetKubeconfigSecret(context,executionId) // if err != nil { @@ -384,15 +381,14 @@ func (k *KubernetesService) CreateKubeconfigSecret(context context.Context, kube // _ = err // } - resp, err := k.Set.CoreV1(). - Secrets(executionId). - Apply(context, - secretApplyConfig, - metav1.ApplyOptions{ - FieldManager: "admiralty-manager", - }) - + 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) @@ -411,7 +407,7 @@ func (k *KubernetesService) CreateKubeconfigSecret(context context.Context, kube 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{}) + Get(context, "kube-secret-"+getConcatenatedName(peerId, executionId), metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { @@ -459,23 +455,22 @@ func dynamicClientApply(executionId string, resourceName string, resourceDefinit } res, err := cli.Resource(resourceDefinition). - Namespace(executionId). - Apply(ctx, - resourceName, - &unstructured.Unstructured{Object: object}, - metav1.ApplyOptions{ - FieldManager: "kubectl-client-side-apply", - }, - ) + 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 + 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) + resByte, err := json.Marshal(res) if err != nil { // fmt.Println("Error trying to create a Source on remote cluster : ", err , " : ", res) return nil, err @@ -485,26 +480,40 @@ func dynamicClientApply(executionId string, resourceName string, resourceDefinit } -func putCDRapiKube(client kubernetes.Clientset, ctx context.Context, path string, body []byte, params ...map[string]string) ([]byte, error){ - req := client.RESTClient(). - Post(). - AbsPath(path). - Body(body) +func (k *KubernetesService) CheckHealth() error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() - for _, param := range params { - for k, v := range param { - req = req.Param(k, v) + // 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) + } } } - resp, err := req.DoRaw(ctx) - + // 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 { - fmt.Println("Error from k8s API when posting "+string(body)+" to "+path+" : ", err) - return nil, err + return fmt.Errorf("failed to list pods: %v", err) } - - return resp, nil + 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 @@ -526,7 +535,7 @@ func (k *KubernetesService) GetOneNode(context context.Context, executionID stri } for _, node := range res.Items { - if isNode := strings.Contains(node.Name, "admiralty-"+ executionID +"-target-"+ concatenatedName + "-"); isNode { + if isNode := strings.Contains(node.Name, "admiralty-"+executionID+"-target-"+concatenatedName+"-"); isNode { return &node, nil } } @@ -534,7 +543,6 @@ func (k *KubernetesService) GetOneNode(context context.Context, executionID stri return nil, 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 diff --git a/infrastructure/prometheus.go b/infrastructure/prometheus.go new file mode 100644 index 0000000..27232ff --- /dev/null +++ b/infrastructure/prometheus.go @@ -0,0 +1,176 @@ +package infrastructure + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "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/compute_units" +) + +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(bookingID 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. + bAccess := oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), "", "", []string{}, nil) + book := bAccess.LoadOne(bookingID) + if book.Err != "" { + fmt.Errorf("stop because of empty : %s", book.Err) + return nil, metrics + } + cUAccess := oclib.NewRequest(oclib.LibDataEnum(oclib.COMPUTE_UNITS), "", "", []string{}, nil) + cRAccess := oclib.NewRequest(oclib.LibDataEnum(oclib.COMPUTE_RESOURCE), "", "", []string{}, nil) + + rr := cRAccess.LoadOne(book.Data.(*booking.Booking).ResourceID) + if rr.Err != "" { + fmt.Errorf("can't proceed because of unfound resource %s : %s", book.Data.(*booking.Booking).ResourceID, rr.Err) + return book.Data.(*booking.Booking), 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}}, + }, + }, "", false) + if res.Err != "" { + continue + } + for _, r := range res.Data { + // TODO watch out ... to not exec on an absent datacenter... + if r.(*compute_units.ComputeUnits).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.(*compute_units.ComputeUnits).MonitorPath, expr, book.Data.(*booking.Booking).ExecutionsID)) + } + metrics[instance.Name] = snapshot + }() + } + } + + wg.Wait() + return book.Data.(*booking.Booking), metrics +} + +func (p *PrometheusService) Stream(bookingID string, end *time.Time, interval time.Duration, flusher http.Flusher, encoder *json.Encoder) { + if end != nil { + max := 100 + count := 0 + mets := map[string][]models.MetricsSnapshot{} + for time.Now().Before(*end) { + go func() { + count++ + book, metrics := p.Call(bookingID) + for k, v := range metrics { + if me, ok := mets[k]; !ok { + mets[k] = []models.MetricsSnapshot{v} + } else { + me = append(me, v) + mets[k] = me + } + } + encoder.Encode(metrics) + flusher.Flush() + if count == max { + 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) + } + }() + time.Sleep(interval) + } + } else { + // todo an anchor... detecting the end of a task. + } +} + +// should add a datacenter... under juridiction... of opencloud...