oc-catalog/models/workspace.go

419 lines
8.9 KiB
Go

package models
import (
"context"
"errors"
"cloud.o-forge.io/core/oc-catalog/models/rtype"
"cloud.o-forge.io/core/oc-catalog/services"
"github.com/beego/beego/v2/core/logs"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
// Assure consistency by using a const which refers to the MongoDB entry name
// Workspace.Projects
const WorkflowDB = "workflows"
type Workspace struct {
UserID string `bson:"_id" json:"user_id"`
Workflows map[string]Workflow //WorkflowDB
// ID: rtype
Data []string `json:"data"`
Computing []string `json:"computing"`
Datacenter []string `json:"datacenter"`
Storage []string `json:"storage"`
}
type ResourceModel interface {
getRtype() rtype.Rtype
getName() string
}
func (w Workspace) getRtype(rID string) (resModel rtype.Rtype) {
for _, compVal := range w.Computing {
if compVal == rID {
return rtype.COMPUTING
}
}
for _, datVal := range w.Data {
if datVal == rID {
return rtype.DATA
}
}
for _, storVal := range w.Storage {
if storVal == rID {
return rtype.STORAGE
}
}
for _, datcentVal := range w.Datacenter {
if datcentVal == rID {
return rtype.DATACENTER
}
}
return rtype.INVALID
}
func (w *Workspace) GetResources() map[rtype.Rtype][]string {
return map[rtype.Rtype][]string{
rtype.DATA: w.Data,
rtype.COMPUTING: w.Computing,
rtype.STORAGE: w.Storage,
rtype.DATACENTER: w.Datacenter,
}
}
func (w *Workspace) GetWorkflow(workflowName string) *Workflow {
var proj Workflow
proj = w.Workflows[workflowName]
return &proj
}
func (w *Workspace) GetWorkflows() []string {
if len(w.Workflows) == 0 {
return nil
}
workflowNames := make([]string, len(w.Workflows))
i := 0
for k := range w.Workflows {
workflowNames[i] = k
i++
}
return workflowNames
}
type WorkspaceModel struct {
UserID string `bson:"_id" json:"user_id"`
Data []DataModel `json:"data"`
Computing []ComputingModel `json:"computing"`
Datacenter []DatacenterModel `json:"datacenter"`
Storage []StorageModel `json:"storage"`
}
func ListFullWorkspace(userID string) (*WorkspaceModel, error) {
ws := GetWorkspace(userID)
if ws == nil {
return nil, errors.New("Internal error")
}
fws := &WorkspaceModel{
UserID: ws.UserID,
Data: []DataModel{},
Computing: []ComputingModel{},
Datacenter: []DatacenterModel{},
Storage: []StorageModel{},
}
pipeline := []primitive.M{
{"$match": primitive.M{"_id": userID}},
{"$lookup": primitive.M{
"localField": "data",
"from": services.MngoNamesCollection.DATA,
"foreignField": "_id",
"as": "data",
}},
{"$lookup": primitive.M{
"localField": "computing",
"from": services.MngoNamesCollection.COMPUTING,
"foreignField": "_id",
"as": "computing",
}},
{"$lookup": primitive.M{
"localField": "datacenter",
"from": services.MngoNamesCollection.DATACENTER,
"foreignField": "_id",
"as": "datacenter",
}},
{"$lookup": primitive.M{
"localField": "storage",
"from": services.MngoNamesCollection.STORAGE,
"foreignField": "_id",
"as": "storage",
}},
}
ret, err := services.MngoCollWorkspace.Aggregate(services.MngoCtx, pipeline)
if err != nil {
message := "Couldn't obtain subobjects"
logs.Debug(message + "; " + err.Error())
return nil, errors.New(message)
}
if ret.RemainingBatchLength() == 1 {
ret.Next(context.Background())
ret.Decode(&fws)
}
return fws, nil
}
// Contains tells whether a contains x.
func contains(a []string, x string) bool {
for _, n := range a {
if x == n {
return true
}
}
return false
}
func RemoveResource(userID, rID, rType string) error {
rIDObj, err := primitive.ObjectIDFromHex(rID)
if err != nil {
message := "ID " + rID + " is not valid"
logs.Debug(message + "; " + err.Error())
return errors.New(message)
}
result, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx,
primitive.M{"_id": userID},
primitive.M{"$pull": primitive.M{rType: rIDObj}},
)
if err != nil {
message := err.Error()
logs.Debug(message)
return errors.New(message)
}
if result.MatchedCount == 0 {
message := "No user " + userID + " in workspace"
logs.Debug(message)
return errors.New(message)
}
if result.ModifiedCount == 0 {
message := "No rID " + rID + " in rtype " + rType
logs.Debug(message)
return errors.New(message)
}
return nil
}
func (w *Workspace) updateDB() (err error) {
_, err = services.MngoCollWorkspace.ReplaceOne(services.MngoCtx,
primitive.M{"_id": w.UserID},
w,
)
return
}
func (w *Workspace) NewResource(rID string, rType string) (err error) {
var targetArray *[]string
switch rType {
case rtype.DATA.String():
targetArray = &w.Data
case rtype.COMPUTING.String():
targetArray = &w.Computing
case rtype.STORAGE.String():
targetArray = &w.Storage
case rtype.DATACENTER.String():
targetArray = &w.Datacenter
default:
return errors.New("Rtype " + rType + " is not valid")
}
for _, models := range *targetArray {
if models == rID {
return errors.New("Resource " + rID + " of type " + rType +
" is already registered for user " + w.UserID)
}
}
*targetArray = append(*targetArray, rID)
w.updateDB()
return
}
func AddResource(userID, rID, rType string) (err error) {
var rIDObj *primitive.ObjectID
if rIDObj, err = IsValidResource(rID, rType); err != nil {
return err
}
//TODO: Maybe operate directly in the DB instead retriving the full object?
userWorkspace := GetWorkspace(userID)
// Exist in the DB
if userWorkspace != nil {
var targetArray []string
switch rType {
case rtype.DATA.String():
targetArray = userWorkspace.Data
case rtype.COMPUTING.String():
targetArray = userWorkspace.Computing
case rtype.STORAGE.String():
targetArray = userWorkspace.Storage
case rtype.DATACENTER.String():
targetArray = userWorkspace.Datacenter
default:
message := "Rtype " + rType + " is not valid"
logs.Debug(message)
return errors.New(message)
}
if ok := contains(targetArray, rID); ok {
// Element already registered
message := "Resource " + rID + " of type " + rType +
" is already registered for user " + userID
logs.Debug(message)
return errors.New(message)
}
// New element
// userWorkspace.ResourceList[rID] = rType
_, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx,
primitive.M{"_id": userID},
primitive.M{"$push": primitive.M{rType: rIDObj}},
)
if err != nil {
message := "Internal error when updating in DB"
logs.Debug(message + "; " + err.Error())
return errors.New(message)
}
return nil
}
return errors.New("Internal error")
}
func rTypeToCollection(rType string) (*mongo.Collection, error) {
switch rType {
case rtype.DATA.String():
return services.MngoCollData, nil
case rtype.COMPUTING.String():
return services.MngoCollComputing, nil
case rtype.DATACENTER.String():
return services.MngoCollDatacenter, nil
case rtype.STORAGE.String():
return services.MngoCollStorage, nil
}
message := rType + " is not a valid resource type"
logs.Debug(message)
return nil, errors.New(message)
}
func IsValidResource(rID, rType string) (*primitive.ObjectID, error) {
targetColl, err := rTypeToCollection(rType)
if err != nil {
return nil, err
}
rIDObj, err := primitive.ObjectIDFromHex(rID)
if err != nil {
message := "ID " + rID + " is not valid"
logs.Debug(message + "; " + err.Error())
return nil, errors.New(message)
}
result := targetColl.FindOne(services.MngoCtx, primitive.M{"_id": rIDObj})
if result.Err() != nil {
message := "ID " + rID + " doesn't exist for resource type " + rType
logs.Debug(message + "; " + result.Err().Error())
return nil, errors.New(message)
}
return &rIDObj, nil
}
func GetAllWorkspaces() <-chan *Workspace {
ch := make(chan *Workspace)
go func() {
cursor, err := services.MngoCollWorkspace.Find(services.MngoCtx, primitive.M{})
if err != nil {
logs.Error(cursor.Err())
close(ch)
}
for cursor.Next(services.MngoCtx) {
var item Workspace
if err = cursor.Decode(&item); err != nil {
logs.Error(err)
close(ch)
}
ch <- &item
}
close(ch) // Remember to close or the loop never ends!
}()
return ch
}
func (w *Workspace) GetAllWorkspacesProjects() <-chan *Workflow {
ch := make(chan *Workflow)
go func() {
for _, wproj := range w.Workflows {
ch <- &wproj
}
close(ch)
}()
return ch
}
func GetWorkspace(userID string) (retObj *Workspace) {
if err := services.MngoCollWorkspace.FindOne(services.MngoCtx, primitive.M{"_id": userID}).Decode(&retObj); err != nil {
logs.Error(err.Error())
return nil
}
return
}
func NewWorkspace(userID string) (*Workspace, error) {
newWsp := &Workspace{
UserID: userID,
Data: []string{},
Computing: []string{},
Datacenter: []string{},
Storage: []string{},
}
_, err := services.MngoCollWorkspace.InsertOne(services.MngoCtx, newWsp)
if err != nil {
logs.Warning(err.Error())
return nil, err
}
return newWsp, nil
}