Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add initial implementation of mesh model types and filters #630

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions models/meshmodel/core/types/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package types

type CapabilityType string

const (
ComponentDefinition CapabilityType = "component"
PolicyDefinition CapabilityType = "policy"
RelationshipDefinition CapabilityType = "relationship"
Model CapabilityType = "model"
)

// Each entity will have it's own Filter implementation via which it exposes the nobs and dials to fetch entities
type Filter interface {
Create(map[string]interface{})
}
82 changes: 82 additions & 0 deletions models/meshmodel/core/v1alpha1/category.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package v1alpha1

import (
"encoding/json"
"sync"

"github.com/google/uuid"
"github.com/layer5io/meshkit/database"
"gorm.io/gorm"
)

var categoryCreationLock sync.Mutex //Each model will perform a check and if the category already doesn't exist, it will create a category. This lock will make sure that there are no race conditions.

// swagger:response Category
type Category struct {
ID uuid.UUID `json:"-" yaml:"-"`
Name string `json:"name"`
Metadata map[string]interface{} `json:"metadata" yaml:"metadata"`
}
type CategoryDB struct {
ID uuid.UUID `json:"-"`
Name string `json:"categoryName" gorm:"categoryName"`
Metadata []byte `json:"categoryMetadata" gorm:"categoryMetadata"`
}
type CategoryFilter struct {
Name string
OrderOn string
Greedy bool
Sort string //asc or desc. Default behavior is asc
Limit int //If 0 or unspecified then all records are returned and limit is not used
Offset int
}

const DefaultCategory = "Miscellaneous"

// Create the filter from map[string]interface{}
func (cf *CategoryFilter) Create(m map[string]interface{}) {
if m == nil {
return
}
cf.Name = m["name"].(string)
}
func CreateCategory(db *database.Handler, cat Category) (uuid.UUID, error) {
if cat.Name == "" {
cat.Name = DefaultCategory
}
byt, err := json.Marshal(cat)
if err != nil {
return uuid.UUID{}, err
}
catID := uuid.NewSHA1(uuid.UUID{}, byt)
var category CategoryDB
categoryCreationLock.Lock()
defer categoryCreationLock.Unlock()
err = db.First(&category, "id = ?", catID).Error
if err != nil && err != gorm.ErrRecordNotFound {
return uuid.UUID{}, err
}
if err == gorm.ErrRecordNotFound { //The category is already not present and needs to be inserted
cat.ID = catID
catdb := cat.GetCategoryDB(db)
err = db.Create(&catdb).Error
if err != nil {
return uuid.UUID{}, err
}
return catdb.ID, nil
}
return category.ID, nil
}

func (cdb *CategoryDB) GetCategory(db *database.Handler) (cat Category) {
cat.ID = cdb.ID
cat.Name = cdb.Name
_ = json.Unmarshal(cdb.Metadata, &cat.Metadata)
return
}
func (c *Category) GetCategoryDB(db *database.Handler) (catdb CategoryDB) {
catdb.ID = c.ID
catdb.Name = c.Name
catdb.Metadata, _ = json.Marshal(c.Metadata)
return
}
213 changes: 213 additions & 0 deletions models/meshmodel/core/v1alpha1/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package v1alpha1

import (
"encoding/json"
"fmt"
"time"

"github.com/google/uuid"
"github.com/layer5io/meshkit/database"
"github.com/layer5io/meshkit/models/meshmodel/core/types"
"gorm.io/gorm/clause"
)

type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion"`
}
type ComponentFormat string

const (
JSON ComponentFormat = "JSON"
YAML ComponentFormat = "YAML"
CUE ComponentFormat = "CUE"
)

// swagger:response ComponentDefinition
// use NewComponent function for instantiating
type ComponentDefinition struct {
ID uuid.UUID `json:"-"`
TypeMeta
DisplayName string `json:"displayName" gorm:"displayName"`
Format ComponentFormat `json:"format" yaml:"format"`
HostName string `json:"hostname"`
HostID uuid.UUID `json:"hostID"`
DisplayHostName string `json:"displayhostname"`
Metadata map[string]interface{} `json:"metadata" yaml:"metadata"`
Model Model `json:"model"`
Schema string `json:"schema,omitempty" yaml:"schema"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
}
type ComponentDefinitionDB struct {
ID uuid.UUID `json:"-"`
ModelID uuid.UUID `json:"-" gorm:"modelID"`
TypeMeta
DisplayName string `json:"displayName" gorm:"displayName"`
Format ComponentFormat `json:"format" yaml:"format"`
Metadata []byte `json:"metadata" yaml:"metadata"`
Schema string `json:"schema,omitempty" yaml:"schema"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
}

func (c ComponentDefinition) Type() types.CapabilityType {
return types.ComponentDefinition
}
func (c ComponentDefinition) GetID() uuid.UUID {
return c.ID
}
func emptySchemaCheck(schema string) (valid bool) {
if schema == "" {
return
}
m := make(map[string]interface{})
_ = json.Unmarshal([]byte(schema), &m)
if m["properties"] == nil {
return
}
valid = true
return
}
func CreateComponent(db *database.Handler, c ComponentDefinition) (uuid.UUID, uuid.UUID, error) {
c.ID = uuid.New()
mid, err := CreateModel(db, c.Model)
if err != nil {
return uuid.UUID{}, uuid.UUID{}, err
}

if !emptySchemaCheck(c.Schema) {
c.Metadata["hasInvalidSchema"] = true
}
cdb := c.GetComponentDefinitionDB()
cdb.ModelID = mid
err = db.Create(&cdb).Error
return c.ID, mid, err
}
func GetMeshModelComponents(db *database.Handler, f ComponentFilter) (c []ComponentDefinition, count int64, unique int) {
type componentDefinitionWithModel struct {
ComponentDefinitionDB
ModelDB
CategoryDB
}

countUniqueComponents := func(components []componentDefinitionWithModel) int {
set := make(map[string]struct{})
for _, model := range components {
key := model.ComponentDefinitionDB.Kind + "@" + model.APIVersion + "@" + model.ModelDB.Name + "@" + model.ModelDB.Version
if _, ok := set[key]; !ok {
set[key] = struct{}{}
}
}
return len(set)
}

var componentDefinitionsWithModel []componentDefinitionWithModel
finder := db.Model(&ComponentDefinitionDB{}).
Select("component_definition_dbs.*, model_dbs.*,category_dbs.*").
Joins("JOIN model_dbs ON component_definition_dbs.model_id = model_dbs.id").
Joins("JOIN category_dbs ON model_dbs.category_id = category_dbs.id") //
if f.Greedy {
if f.Name != "" && f.DisplayName != "" {
finder = finder.Where("component_definition_dbs.kind LIKE ? OR display_name LIKE ?", "%"+f.Name+"%", f.DisplayName+"%")
} else if f.Name != "" {
finder = finder.Where("component_definition_dbs.kind LIKE ?", "%"+f.Name+"%")
} else if f.DisplayName != "" {
finder = finder.Where("component_definition_dbs.display_name LIKE ?", "%"+f.DisplayName+"%")
}
} else {
if f.Name != "" {
finder = finder.Where("component_definition_dbs.kind = ?", f.Name)
}
if f.DisplayName != "" {
finder = finder.Where("component_definition_dbs.display_name = ?", f.DisplayName)
}
}
if f.ModelName != "" && f.ModelName != "all" {
finder = finder.Where("model_dbs.name = ?", f.ModelName)
}
if f.APIVersion != "" {
finder = finder.Where("component_definition_dbs.api_version = ?", f.APIVersion)
}
if f.CategoryName != "" {
finder = finder.Where("category_dbs.name = ?", f.CategoryName)
}
if f.Version != "" {
finder = finder.Where("model_dbs.version = ?", f.Version)
}
if f.OrderOn != "" {
if f.Sort == "desc" {
finder = finder.Order(clause.OrderByColumn{Column: clause.Column{Name: f.OrderOn}, Desc: true})
} else {
finder = finder.Order(f.OrderOn)
}
}

finder.Count(&count)

finder = finder.Offset(f.Offset)
if f.Limit != 0 {
finder = finder.Limit(f.Limit)
}
err := finder.
Scan(&componentDefinitionsWithModel).Error
if err != nil {
fmt.Println(err.Error()) //for debugging
}
for _, cm := range componentDefinitionsWithModel {
if f.Trim {
cm.Schema = ""
}
c = append(c, cm.ComponentDefinitionDB.GetComponentDefinition(cm.ModelDB.GetModel(cm.CategoryDB.GetCategory(db))))
}

unique = countUniqueComponents(componentDefinitionsWithModel)

return c, count, unique
}

type ComponentFilter struct {
Name string
APIVersion string
Greedy bool //when set to true - instead of an exact match, name will be prefix matched
Trim bool //when set to true - the schema is not returned
DisplayName string
ModelName string
CategoryName string
Version string
Sort string //asc or desc. Default behavior is asc
OrderOn string
Limit int //If 0 or unspecified then all records are returned and limit is not used
Offset int
}

// Create the filter from map[string]interface{}
func (cf *ComponentFilter) Create(m map[string]interface{}) {
if m == nil {
return
}
cf.Name = m["name"].(string)
}

func (cmd *ComponentDefinitionDB) GetComponentDefinition(model Model) (c ComponentDefinition) {
c.ID = cmd.ID
c.TypeMeta = cmd.TypeMeta
c.Format = cmd.Format
c.DisplayName = cmd.DisplayName
if c.Metadata == nil {
c.Metadata = make(map[string]interface{})
}
_ = json.Unmarshal(cmd.Metadata, &c.Metadata)
c.Schema = cmd.Schema
c.Model = model
return
}
func (c *ComponentDefinition) GetComponentDefinitionDB() (cmd ComponentDefinitionDB) {
cmd.ID = c.ID
cmd.TypeMeta = c.TypeMeta
cmd.Format = c.Format
cmd.Metadata, _ = json.Marshal(c.Metadata)
cmd.DisplayName = c.DisplayName
cmd.Schema = c.Schema
return
}
37 changes: 37 additions & 0 deletions models/meshmodel/core/v1alpha1/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package v1alpha1

import "github.com/google/uuid"

type MeshModelHostsWithEntitySummary struct {
ID uuid.UUID `json:"id"`
Hostname string `json:"hostname"`
Port int `json:"port"`
Summary EntitySummary `json:"summary"`
}
type EntitySummary struct {
Models int64 `json:"models"`
Components int64 `json:"components"`
Relationships int64 `json:"relationships"`
Policies int64 `json:"policies"`
}
type MesheryHostSummaryDB struct {
HostID uuid.UUID `json:"-" gorm:"id"`
Hostname string `json:"-" gorm:"hostname"`
Port int `json:"-" gorm:"port"`
Models int64 `json:"-" gorm:"models"`
Components int64 `json:"-" gorm:"components"`
Relationships int64 `json:"-" gorm:"relationships"`
Policies int64 `json:"-" gorm:"policies"`
}

type HostFilter struct {
Name string
Greedy bool //when set to true - instead of an exact match, name will be prefix matched
Trim bool //when set to true - the schema is not returned
DisplayName string
Version string
Sort string //asc or desc. Default behavior is asc
OrderOn string
Limit int //If 0 or unspecified then all records are returned and limit is not used
Offset int
}
Loading
Loading