package oclib import ( "errors" "fmt" "strings" "runtime/debug" "cloud.o-forge.io/core/oc-lib/config" "cloud.o-forge.io/core/oc-lib/dbs" "cloud.o-forge.io/core/oc-lib/dbs/mongo" "cloud.o-forge.io/core/oc-lib/logs" "cloud.o-forge.io/core/oc-lib/models" "cloud.o-forge.io/core/oc-lib/models/collaborative_area" "cloud.o-forge.io/core/oc-lib/models/collaborative_area/rules/rule" "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/resource_model" "cloud.o-forge.io/core/oc-lib/models/resources/data" "cloud.o-forge.io/core/oc-lib/models/resources/datacenter" "cloud.o-forge.io/core/oc-lib/models/resources/processing" "cloud.o-forge.io/core/oc-lib/models/resources/storage" w "cloud.o-forge.io/core/oc-lib/models/resources/workflow" "cloud.o-forge.io/core/oc-lib/models/utils" w2 "cloud.o-forge.io/core/oc-lib/models/workflow" "cloud.o-forge.io/core/oc-lib/models/workflow_execution" "cloud.o-forge.io/core/oc-lib/models/workspace" "cloud.o-forge.io/core/oc-lib/tools" beego "github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web/context" "github.com/goraz/onion" "github.com/rs/zerolog" ) type Filters = dbs.Filters type LibDataEnum int // init accessible constant to retrieve data from the database const ( INVALID LibDataEnum = iota DATA_RESOURCE = tools.DATA_RESOURCE PROCESSING_RESOURCE = tools.PROCESSING_RESOURCE STORAGE_RESOURCE = tools.STORAGE_RESOURCE DATACENTER_RESOURCE = tools.DATACENTER_RESOURCE WORKFLOW_RESOURCE = tools.WORKFLOW_RESOURCE WORKFLOW = tools.WORKFLOW WORKSPACE = tools.WORKSPACE WORKFLOW_EXECUTION = tools.WORKFLOW_EXECUTION PEER = tools.PEER SHARED_WORKSPACE = tools.COLLABORATIVE_AREA RULE = tools.RULE BOOKING = tools.BOOKING ) // will turn into standards api hostnames func (d LibDataEnum) API() string { return tools.DefaultAPI[d] } // will turn into standards name func (d LibDataEnum) String() string { return tools.Str[d] } // will turn into enum index func (d LibDataEnum) EnumIndex() int { return int(d) } func IsQueryParamsEquals(input *context.BeegoInput, name string, val interface{}) bool { path := strings.Split(input.URI(), "?") if len(path) >= 2 { uri := strings.Split(path[1], "&") for _, val := range uri { kv := strings.Split(val, "=") if kv[0] == name && fmt.Sprintf("%v", val) == kv[1] { return true } } } return false } // model to define the shallow data structure type LibDataShallow struct { Data []utils.ShallowDBObject `bson:"data" json:"data"` Code int `bson:"code" json:"code"` Err string `bson:"error" json:"error"` } // model to define the data structure type LibData struct { Data utils.DBObject `bson:"data" json:"data"` Code int `bson:"code" json:"code"` Err string `bson:"error" json:"error"` } func Init(appName string) { config.SetAppName(appName) // set the app name to the logger to define the main log chan // create a temporary console logger for init logs.SetLogger(logs.CreateLogger("main")) // Load the right config file o := GetConfLoader() // feed the library with the loaded config SetConfig( o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"), o.GetStringDefault("MONGO_DATABASE", "DC_myDC"), o.GetStringDefault("NATS_URL", "nats://localhost:4222"), o.GetStringDefault("LOKI_URL", ""), o.GetStringDefault("LOG_LEVEL", "info"), ) // Beego init beego.BConfig.AppName = appName beego.BConfig.Listen.HTTPPort = o.GetIntDefault("port", 8080) beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" api := &tools.API{} api.Discovered(beego.BeeApp.Handlers.GetAllControllerInfo()) } // // Expose subpackages // /* GetLogger returns the main logger * @return zerolog.Logger */ func GetLogger() zerolog.Logger { return logs.GetLogger() } /* SetConfig will set the config and create a logger according to app configuration and initialize mongo accessor * @param url string * @param database string * @param natsUrl string * @param lokiUrl string * @param logLevel string * @return *Config */ func SetConfig(mongoUrl string, database string, natsUrl string, lokiUrl string, logLevel string) *config.Config { cfg := config.SetConfig(mongoUrl, database, natsUrl, lokiUrl, logLevel) defer func() { if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in Init : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) fmt.Printf("Panic recovered in Init : %v - %v\n", r, string(debug.Stack())) } }() logs.CreateLogger("main") mongo.MONGOService.Init(models.GetModelsNames(), config.GetConfig()) // init the mongo service /* Here we will check if the resource model is already stored in the database If not we will store it Resource model is the model that will define the structure of the resources */ accessor := (&resource_model.ResourceModel{}).GetAccessor(nil) for _, model := range []string{tools.DATA_RESOURCE.String(), tools.PROCESSING_RESOURCE.String(), tools.STORAGE_RESOURCE.String(), tools.DATACENTER_RESOURCE.String(), tools.WORKFLOW_RESOURCE.String()} { data, code, _ := accessor.Search(nil, model) if code == 404 || len(data) == 0 { refs := map[string]string{} m := map[string]resource_model.Model{} // TODO Specify the model for each resource // for now only processing is specified here (not an elegant way) if model == tools.DATA_RESOURCE.String() || model == tools.STORAGE_RESOURCE.String() { refs["path"] = "string" } if model == tools.PROCESSING_RESOURCE.String() { m["command"] = resource_model.Model{ Type: "string", ReadOnly: false, } m["args"] = resource_model.Model{ Type: "string", ReadOnly: false, } m["env"] = resource_model.Model{ Type: "string", ReadOnly: false, } m["volumes"] = resource_model.Model{ Type: "map[string]string", ReadOnly: false, } } accessor.StoreOne(&resource_model.ResourceModel{ ResourceType: model, VarRefs: refs, Model: map[string]map[string]resource_model.Model{ "container": m, }, }) } } return cfg } /* GetConfig will get the config * @return *Config */ func GetConfig() *config.Config { return config.GetConfig() } /* GetConfLoader * Get the configuration loader for the application * Parameters: * - AppName: string : the name of the application * Returns: * - *onion.Onion : the configuration loader * The configuration loader will load the configuration from the following sources: * - the environment variables with the prefix OCAPPNAME_ * - the file /etc/oc/appname.json * - the file ./appname.json * The configuration loader will merge the configuration from the different sources * The configuration loader will give priority to the environment variables * The configuration loader will give priority to the local file over the default file */ func GetConfLoader() *onion.Onion { return config.GetConfLoader() } /* * Search will search for the data in the database * @param filters *dbs.Filters * @param word string * @param collection LibDataEnum * @param c ...*tools.HTTPCaller * @return data LibDataShallow */ func Search(filters *dbs.Filters, word string, collection LibDataEnum, c ...*tools.HTTPCaller) (data LibDataShallow) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in Search : "+fmt.Sprintf("%v", r))) data = LibDataShallow{Data: nil, Code: 500, Err: "Panic recovered in LoadAll : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() var caller *tools.HTTPCaller // define the caller if len(c) > 0 { caller = c[0] } d, code, err := models.Model(collection.EnumIndex()).GetAccessor(caller).Search(filters, word) if err != nil { data = LibDataShallow{Data: d, Code: code, Err: err.Error()} return } data = LibDataShallow{Data: d, Code: code} return } /* * LoadAll will load all the data from the database * @param collection LibDataEnum * @param c ...*tools.HTTPCaller * @return data LibDataShallow */ func LoadAll(collection LibDataEnum, c ...*tools.HTTPCaller) (data LibDataShallow) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in LoadAll : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibDataShallow{Data: nil, Code: 500, Err: "Panic recovered in LoadAll : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() var caller *tools.HTTPCaller // define the caller if len(c) > 0 { caller = c[0] } d, code, err := models.Model(collection.EnumIndex()).GetAccessor(caller).LoadAll() if err != nil { data = LibDataShallow{Data: d, Code: code, Err: err.Error()} return } data = LibDataShallow{Data: d, Code: code} return } /* * LoadOne will load one data from the database * @param collection LibDataEnum * @param id string * @param c ...*tools.HTTPCaller * @return data LibData */ func LoadOne(collection LibDataEnum, id string, c ...*tools.HTTPCaller) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in LoadOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in LoadOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() var caller *tools.HTTPCaller // define the caller if len(c) > 0 { caller = c[0] } d, code, err := models.Model(collection.EnumIndex()).GetAccessor(caller).LoadOne(id) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * UpdateOne will update one data from the database * @param collection LibDataEnum * @param set map[string]interface{} * @param id string * @param c ...*tools.HTTPCaller * @return data LibData */ func UpdateOne(collection LibDataEnum, set map[string]interface{}, id string, c ...*tools.HTTPCaller) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in UpdateOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in UpdateOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() var caller *tools.HTTPCaller // define the caller if len(c) > 0 { caller = c[0] } model := models.Model(collection.EnumIndex()) d, code, err := model.GetAccessor(caller).UpdateOne(model.Deserialize(set), id) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * DeleteOne will delete one data from the database * @param collection LibDataEnum * @param id string * @param c ...*tools.HTTPCaller * @return data LibData */ func DeleteOne(collection LibDataEnum, id string, c ...*tools.HTTPCaller) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in DeleteOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in DeleteOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() var caller *tools.HTTPCaller // define the caller if len(c) > 0 { caller = c[0] } d, code, err := models.Model(collection.EnumIndex()).GetAccessor(caller).DeleteOne(id) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * StoreOne will store one data from the database * @param collection LibDataEnum * @param object map[string]interface{} * @param c ...*tools.HTTPCaller * @return data LibData */ func StoreOne(collection LibDataEnum, object map[string]interface{}, c ...*tools.HTTPCaller) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in StoreOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in StoreOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() var caller *tools.HTTPCaller // define the caller if len(c) > 0 { caller = c[0] } model := models.Model(collection.EnumIndex()) d, code, err := model.GetAccessor(caller).StoreOne(model.Deserialize(object)) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * CopyOne will copy one data from the database * @param collection LibDataEnum * @param object map[string]interface{} * @param c ...*tools.HTTPCaller * @return data LibData */ func CopyOne(collection LibDataEnum, object map[string]interface{}, c ...*tools.HTTPCaller) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in CopyOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in UpdateOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() var caller *tools.HTTPCaller // define the caller if len(c) > 0 { caller = c[0] } model := models.Model(collection.EnumIndex()) d, code, err := model.GetAccessor(caller).CopyOne(model.Deserialize(object)) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } // ================ CAST ========================= // func (l *LibData) ToDataResource() *data.DataResource { if l.Data.GetAccessor(nil).GetType() == tools.DATA_RESOURCE.String() { return l.Data.(*data.DataResource) } return nil } func (l *LibData) ToDatacenterResource() *datacenter.DatacenterResource { if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.DATACENTER_RESOURCE.String() { return l.Data.(*datacenter.DatacenterResource) } return nil } func (l *LibData) ToStorageResource() *storage.StorageResource { if l.Data.GetAccessor(nil).GetType() == tools.STORAGE_RESOURCE.String() { return l.Data.(*storage.StorageResource) } return nil } func (l *LibData) ToProcessingResource() *processing.ProcessingResource { if l.Data.GetAccessor(nil).GetType() == tools.PROCESSING_RESOURCE.String() { return l.Data.(*processing.ProcessingResource) } return nil } func (l *LibData) ToWorkflowResource() *w.WorkflowResource { if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW_RESOURCE.String() { return l.Data.(*w.WorkflowResource) } return nil } func (l *LibData) ToPeer() *peer.Peer { if l.Data.GetAccessor(nil).GetType() == tools.PEER.String() { return l.Data.(*peer.Peer) } return nil } func (l *LibData) ToWorkflow() *w2.Workflow { if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW.String() { return l.Data.(*w2.Workflow) } return nil } func (l *LibData) ToWorkspace() *workspace.Workspace { if l.Data.GetAccessor(nil).GetType() == tools.WORKSPACE.String() { return l.Data.(*workspace.Workspace) } return nil } func (l *LibData) ToCollaborativeArea() *collaborative_area.CollaborativeArea { if l.Data.GetAccessor(nil).GetType() == tools.COLLABORATIVE_AREA.String() { return l.Data.(*collaborative_area.CollaborativeArea) } return nil } func (l *LibData) ToRule() *rule.Rule { if l.Data.GetAccessor(nil).GetType() == tools.COLLABORATIVE_AREA.String() { return l.Data.(*rule.Rule) } return nil } func (l *LibData) ToWorkflowExecution() *workflow_execution.WorkflowExecution { if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW_EXECUTION.String() { return l.Data.(*workflow_execution.WorkflowExecution) } return nil }