| @@ -518,13 +518,21 @@ type PoolInfo struct { | |||||
| PoolType string `json:"pool_type"` | PoolType string `json:"pool_type"` | ||||
| } | } | ||||
| type CommitImageParams struct { | |||||
| type CommitImageCloudBrainParams struct { | |||||
| Ip string `json:"ip"` | Ip string `json:"ip"` | ||||
| TaskContainerId string `json:"taskContainerId"` | TaskContainerId string `json:"taskContainerId"` | ||||
| ImageTag string `json:"imageTag"` | ImageTag string `json:"imageTag"` | ||||
| ImageDescription string `json:"imageDescription"` | ImageDescription string `json:"imageDescription"` | ||||
| } | } | ||||
| type CommitImageParams struct { | |||||
| CommitImageCloudBrainParams | |||||
| IsPrivate bool | |||||
| Topics []string | |||||
| CloudBrainType int | |||||
| UID int64 | |||||
| } | |||||
| type CommitImageResult struct { | type CommitImageResult struct { | ||||
| Code string `json:"code"` | Code string `json:"code"` | ||||
| Msg string `json:"msg"` | Msg string `json:"msg"` | ||||
| @@ -0,0 +1,415 @@ | |||||
| package models | |||||
| import ( | |||||
| "fmt" | |||||
| "strings" | |||||
| "xorm.io/builder" | |||||
| "code.gitea.io/gitea/modules/timeutil" | |||||
| ) | |||||
| const RECOMMOND_TYPE = 5 | |||||
| const NORMAL_TYPE = 0 | |||||
| type Image struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| Type int `xorm:"INDEX NOT NULL"` //0 normal 5官方推荐,中间值保留为后续扩展 | |||||
| CloudbrainType int `xorm:"INDEX NOT NULL"` //0 云脑一 1云脑二 | |||||
| UID int64 `xorm:"INDEX NOT NULL"` | |||||
| IsPrivate bool `xorm:"INDEX NOT NULL"` | |||||
| Tag string `xorm:"varchar(100) UNIQUE"` | |||||
| Description string `xorm:"varchar(765)"` | |||||
| Topics []string `xorm:"TEXT JSON"` | |||||
| Place string `xorm:"varchar(300)"` | |||||
| NumStars int `xorm:"NOT NULL DEFAULT 0"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| } | |||||
| type ImageStar struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| UID int64 `xorm:"UNIQUE(s)"` | |||||
| ImageID int64 `xorm:"UNIQUE(s)"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
| } | |||||
| type ImageTopic struct { | |||||
| ID int64 | |||||
| Name string `xorm:"UNIQUE VARCHAR(105)"` | |||||
| ImageCount int | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| } | |||||
| type ImageTopicRelation struct { | |||||
| ImageID int64 `xorm:"UNIQUE(s)"` | |||||
| TopicID int64 `xorm:"UNIQUE(s)"` | |||||
| } | |||||
| type SearchImageOptions struct { | |||||
| Keyword string | |||||
| UID int64 | |||||
| IncludePublic bool | |||||
| IncludeStarByMe bool | |||||
| IncludeCustom bool | |||||
| CodeLanguage string | |||||
| Framework string | |||||
| CudaVersion string | |||||
| ListOptions | |||||
| SearchOrderBy | |||||
| } | |||||
| type ErrorImageTagExist struct { | |||||
| Tag string | |||||
| } | |||||
| func (err ErrorImageTagExist) Error() string { | |||||
| return fmt.Sprintf("Image already exists [tag: %s]", err.Tag) | |||||
| } | |||||
| type ErrImageNotExist struct { | |||||
| ID int64 | |||||
| } | |||||
| func (err ErrImageNotExist) Error() string { | |||||
| return fmt.Sprintf("Image does not exist [id: %d]", err.ID) | |||||
| } | |||||
| func IsErrImageTagExist(err error) bool { | |||||
| _, ok := err.(ErrorImageTagExist) | |||||
| return ok | |||||
| } | |||||
| func IsImageExist(tag string) (bool, error) { | |||||
| return x.Exist(&Image{ | |||||
| Tag: tag, | |||||
| }) | |||||
| } | |||||
| type FindImageTopicOptions struct { | |||||
| ListOptions | |||||
| ImageID int64 | |||||
| Keyword string | |||||
| } | |||||
| func (opts *FindImageTopicOptions) toConds() builder.Cond { | |||||
| var cond = builder.NewCond() | |||||
| if opts.ImageID > 0 { | |||||
| cond = cond.And(builder.Eq{"image_topic.image_id": opts.ImageID}) | |||||
| } | |||||
| if opts.Keyword != "" { | |||||
| cond = cond.And(builder.Like{"image_topic.name", strings.ToLower(opts.Keyword)}) | |||||
| } | |||||
| return cond | |||||
| } | |||||
| func GetImageByID(id int64) (*Image, error) { | |||||
| rel := new(Image) | |||||
| has, err := x. | |||||
| ID(id). | |||||
| Get(rel) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, ErrImageNotExist{id} | |||||
| } | |||||
| return rel, nil | |||||
| } | |||||
| func FindImageTopics(opts *FindImageTopicOptions) (topics []*ImageTopic, err error) { | |||||
| sess := x.Select("image_topic.*").Where(opts.toConds()) | |||||
| if opts.ImageID > 0 { | |||||
| sess.Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id") | |||||
| } | |||||
| if opts.PageSize != 0 && opts.Page != 0 { | |||||
| sess = opts.setSessionPagination(sess) | |||||
| } | |||||
| return topics, sess.Desc("image_topic.image_count").Find(&topics) | |||||
| } | |||||
| func SaveImageTopics(imageID int64, topicNames ...string) error { | |||||
| topics, err := FindImageTopics(&FindImageTopicOptions{ | |||||
| ImageID: imageID, | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| if err := sess.Begin(); err != nil { | |||||
| return err | |||||
| } | |||||
| var addedTopicNames []string | |||||
| for _, topicName := range topicNames { | |||||
| if strings.TrimSpace(topicName) == "" { | |||||
| continue | |||||
| } | |||||
| var found bool | |||||
| for _, t := range topics { | |||||
| if strings.EqualFold(topicName, t.Name) { | |||||
| found = true | |||||
| break | |||||
| } | |||||
| } | |||||
| if !found { | |||||
| addedTopicNames = append(addedTopicNames, topicName) | |||||
| } | |||||
| } | |||||
| var removeTopics []*ImageTopic | |||||
| for _, t := range topics { | |||||
| var found bool | |||||
| for _, topicName := range topicNames { | |||||
| if strings.EqualFold(topicName, t.Name) { | |||||
| found = true | |||||
| break | |||||
| } | |||||
| } | |||||
| if !found { | |||||
| removeTopics = append(removeTopics, t) | |||||
| } | |||||
| } | |||||
| for _, topicName := range addedTopicNames { | |||||
| _, err := addTopicByNameToImage(sess, imageID, topicName) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| for _, topic := range removeTopics { | |||||
| err := removeTopicFromImage(sess, imageID, topic) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| topicNames = make([]string, 0, 25) | |||||
| if err := sess.Table("image_topic").Cols("name"). | |||||
| Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id"). | |||||
| Where("image_topic_relation.image_id = ?", imageID).Desc("image_topic.image_count").Find(&topicNames); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.ID(imageID).Cols("topics").Update(&Image{ | |||||
| Topics: topicNames, | |||||
| }); err != nil { | |||||
| return err | |||||
| } | |||||
| return sess.Commit() | |||||
| } | |||||
| func addTopicByNameToImage(e Engine, imageID int64, topicName string) (*ImageTopic, error) { | |||||
| var topic ImageTopic | |||||
| has, err := e.Where("name = ?", topicName).Get(&topic) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| if !has { | |||||
| topic.Name = topicName | |||||
| topic.ImageCount = 1 | |||||
| if _, err := e.Insert(&topic); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| } else { | |||||
| topic.ImageCount++ | |||||
| if _, err := e.ID(topic.ID).Cols("image_count").Update(&topic); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| if _, err := e.Insert(&ImageTopicRelation{ | |||||
| ImageID: imageID, | |||||
| TopicID: topic.ID, | |||||
| }); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return &topic, nil | |||||
| } | |||||
| func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error { | |||||
| topic.ImageCount-- | |||||
| if _, err := e.ID(topic.ID).Cols("image_count").Update(topic); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := e.Delete(&ImageTopicRelation{ | |||||
| ImageID: imageId, | |||||
| TopicID: topic.ID, | |||||
| }); err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| /*func SearchImage(opts *SearchImageOptions) ([]*Image, int64, error) { | |||||
| cond := SearchImageCondition(opts) | |||||
| return SearchImageByCondition(opts, cond) | |||||
| }*/ | |||||
| func SearchImageCondition(opts *SearchImageOptions) builder.Cond { | |||||
| var cond = builder.NewCond() | |||||
| if len(opts.Keyword) > 0 { | |||||
| cond = cond.And(builder.Or(builder.Like{"image.tag", opts.Keyword}, builder.Like{"image.description", opts.Keyword})) | |||||
| } | |||||
| if len(opts.CudaVersion) > 0 { | |||||
| cond = cond.And(builder.Eq{"image.cuda_version": opts.CudaVersion}) | |||||
| } | |||||
| if len(opts.CodeLanguage) > 0 { | |||||
| cond = cond.And(builder.Eq{"image.code_language": opts.CodeLanguage}) | |||||
| } | |||||
| if len(opts.Framework) > 0 { | |||||
| cond = cond.And(builder.Eq{"image.framework": opts.Framework}) | |||||
| } | |||||
| if opts.IncludePublic { | |||||
| cond = cond.And(builder.Eq{"image.is_private": false}) | |||||
| } | |||||
| /** | |||||
| if opts.IncludeStarByMe { | |||||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | |||||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | |||||
| if opts.OwnerID > 0 { | |||||
| if len(opts.Keyword) == 0 { | |||||
| cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID}) | |||||
| } else { | |||||
| subCon := builder.NewCond() | |||||
| subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID}, builder.Or(builder.Like{"dataset.title", opts.Keyword}, builder.Like{"dataset.description", opts.Keyword})) | |||||
| cond = cond.Or(subCon) | |||||
| } | |||||
| } | |||||
| } else if opts.OwnerID > 0 { | |||||
| cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID}) | |||||
| if !opts.IsOwner { | |||||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | |||||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | |||||
| } | |||||
| }*/ | |||||
| return cond | |||||
| } | |||||
| /*func SearchImageByCondition(opts *SearchImageOptions, cond builder.Cond) ([]*Image, int64, error) { | |||||
| if opts.Page <= 0 { | |||||
| opts.Page = 1 | |||||
| } | |||||
| var err error | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| datasets := make(DatasetList, 0, opts.PageSize) | |||||
| selectColumnsSql := "distinct dataset.id,dataset.title, dataset.status, dataset.category, dataset.description, dataset.download_times, dataset.license, dataset.task, dataset.release_id, dataset.user_id, dataset.repo_id, dataset.created_unix,dataset.updated_unix,dataset.num_stars" | |||||
| count, err := sess.Distinct("dataset.id").Join("INNER", "repository", "repository.id = dataset.repo_id"). | |||||
| Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). | |||||
| Where(cond).Count(new(Dataset)) | |||||
| if err != nil { | |||||
| return nil, 0, fmt.Errorf("Count: %v", err) | |||||
| } | |||||
| sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id"). | |||||
| Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). | |||||
| Where(cond).OrderBy(opts.SearchOrderBy.String()) | |||||
| if opts.PageSize > 0 { | |||||
| sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | |||||
| } | |||||
| if err = sess.Find(&datasets); err != nil { | |||||
| return nil, 0, fmt.Errorf("Dataset: %v", err) | |||||
| } | |||||
| if err = datasets.loadAttributes(sess); err != nil { | |||||
| return nil, 0, fmt.Errorf("LoadAttributes: %v", err) | |||||
| } | |||||
| return datasets, count, nil | |||||
| }*/ | |||||
| func CreateLocalImage(image *Image) error { | |||||
| _, err := x.Insert(image) | |||||
| return err | |||||
| } | |||||
| //star or unstar Image | |||||
| func StarImage(userID, imageID int64, star bool) error { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| if err := sess.Begin(); err != nil { | |||||
| return err | |||||
| } | |||||
| if star { | |||||
| if isImageStaring(sess, userID, imageID) { | |||||
| return nil | |||||
| } | |||||
| if _, err := sess.Insert(&ImageStar{UID: userID, ImageID: imageID}); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars + 1 WHERE id = ?", imageID); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars + 1 WHERE id = ?", userID); err != nil { | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| if !isImageStaring(sess, userID, imageID) { | |||||
| return nil | |||||
| } | |||||
| if _, err := sess.Delete(&ImageStar{0, userID, imageID, 0}); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars - 1 WHERE id = ?", imageID); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars - 1 WHERE id = ?", userID); err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return sess.Commit() | |||||
| } | |||||
| func IsImageStaring(userID, datasetID int64) bool { | |||||
| return isImageStaring(x, userID, datasetID) | |||||
| } | |||||
| func isImageStaring(e Engine, userID, imageID int64) bool { | |||||
| has, _ := e.Get(&ImageStar{0, userID, imageID, 0}) | |||||
| return has | |||||
| } | |||||
| func RecommendImage(imageId int64, recommond bool) error { | |||||
| image := Image{Type: getRecommondType(recommond)} | |||||
| _, err := x.ID(imageId).Cols("type").Update(image) | |||||
| return err | |||||
| } | |||||
| func getRecommondType(recommond bool) int { | |||||
| if recommond { | |||||
| return RECOMMOND_TYPE | |||||
| } else { | |||||
| return NORMAL_TYPE | |||||
| } | |||||
| } | |||||
| @@ -131,6 +131,8 @@ func init() { | |||||
| new(Dataset), | new(Dataset), | ||||
| new(DatasetStar), | new(DatasetStar), | ||||
| new(Cloudbrain), | new(Cloudbrain), | ||||
| new(Image), | |||||
| new(ImageStar), | |||||
| new(FileChunk), | new(FileChunk), | ||||
| new(BlockChain), | new(BlockChain), | ||||
| new(RecommendOrg), | new(RecommendOrg), | ||||
| @@ -24,9 +24,27 @@ type CreateCloudBrainForm struct { | |||||
| type CommitImageCloudBrainForm struct { | type CommitImageCloudBrainForm struct { | ||||
| Description string `form:"description" binding:"Required"` | Description string `form:"description" binding:"Required"` | ||||
| Tag string `form:"tag" binding:"Required"` | |||||
| Type int `form:"type" binding:"Required"` | |||||
| Tag string `form:"tag" binding:"Required;MaxSize(64)" ` | |||||
| IsPrivate bool `form:"isPrivate" binding:"Required"` | |||||
| Topics string `form:"topics"` | |||||
| } | |||||
| type EditImageCloudBrainForm struct { | |||||
| ID int64 `form:"id" binding:"Required"` | |||||
| Description string `form:"description" binding:"Required"` | |||||
| IsPrivate bool `form:"isPrivate" binding:"Required"` | |||||
| Topics string `form:"topics"` | |||||
| } | } | ||||
| func (f *CreateCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | func (f *CreateCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | ||||
| return validate(errs, ctx.Data, f, ctx.Locale) | return validate(errs, ctx.Data, f, ctx.Locale) | ||||
| } | } | ||||
| func (f *CommitImageCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||||
| } | |||||
| func (f *EditImageCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||||
| } | |||||
| @@ -83,6 +83,18 @@ func isAdminOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error | |||||
| } | } | ||||
| func isAdminOrImageCreater(ctx *context.Context, image *models.Image, err error) bool { | |||||
| if !ctx.IsSigned { | |||||
| return false | |||||
| } | |||||
| if err != nil { | |||||
| return ctx.IsUserSiteAdmin() | |||||
| } else { | |||||
| return ctx.IsUserSiteAdmin() || ctx.User.ID == image.UID | |||||
| } | |||||
| } | |||||
| func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | ||||
| var ID = ctx.Params(":id") | var ID = ctx.Params(":id") | ||||
| @@ -147,6 +159,28 @@ func AdminOrJobCreaterRightForTrain(ctx *context.Context) { | |||||
| } | } | ||||
| func AdminOrImageCreaterRight(ctx *context.Context) { | |||||
| id, err := strconv.ParseInt(ctx.Params(":id"), 10, 64) | |||||
| var image *models.Image | |||||
| if err != nil { | |||||
| log.Error("Get Image by ID failed:%v", err.Error()) | |||||
| } else { | |||||
| image, err = models.GetImageByID(id) | |||||
| if err != nil { | |||||
| log.Error("Get Image by ID failed:%v", err.Error()) | |||||
| return | |||||
| } | |||||
| } | |||||
| if !isAdminOrImageCreater(ctx, image, err) { | |||||
| log.Error("!isAdminOrImageCreater error:%v", err.Error()) | |||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||||
| } | |||||
| } | |||||
| func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue, description string, benchmarkTypeID, benchmarkChildTypeID, resourceSpecId int) error { | func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue, description string, benchmarkTypeID, benchmarkChildTypeID, resourceSpecId int) error { | ||||
| dataActualPath := setting.Attachment.Minio.RealPath + | dataActualPath := setting.Attachment.Minio.RealPath + | ||||
| setting.Attachment.Minio.Bucket + "/" + | setting.Attachment.Minio.Bucket + "/" + | ||||
| @@ -210,6 +210,17 @@ func getQueryString(page int, size int, name string) string { | |||||
| } | } | ||||
| func CommitImage(jobID string, params models.CommitImageParams) error { | func CommitImage(jobID string, params models.CommitImageParams) error { | ||||
| has, err := models.IsImageExist(params.ImageTag) | |||||
| if err != nil { | |||||
| return fmt.Errorf("resty CommitImage: %v", err) | |||||
| } | |||||
| if has { | |||||
| return models.ErrorImageTagExist{ | |||||
| Tag: params.ImageTag, | |||||
| } | |||||
| } | |||||
| checkSetting() | checkSetting() | ||||
| client := getRestyClient() | client := getRestyClient() | ||||
| var result models.CommitImageResult | var result models.CommitImageResult | ||||
| @@ -220,7 +231,7 @@ sendjob: | |||||
| res, err := client.R(). | res, err := client.R(). | ||||
| SetHeader("Content-Type", "application/json"). | SetHeader("Content-Type", "application/json"). | ||||
| SetAuthToken(TOKEN). | SetAuthToken(TOKEN). | ||||
| SetBody(params). | |||||
| SetBody(params.CommitImageCloudBrainParams). | |||||
| SetResult(&result). | SetResult(&result). | ||||
| Post(HOST + "/rest-server/api/v1/jobs/" + jobID + "/commitImage") | Post(HOST + "/rest-server/api/v1/jobs/" + jobID + "/commitImage") | ||||
| @@ -238,7 +249,27 @@ sendjob: | |||||
| return fmt.Errorf("CommitImage err: %s", res.String()) | return fmt.Errorf("CommitImage err: %s", res.String()) | ||||
| } | } | ||||
| return nil | |||||
| err = models.WithTx(func(ctx models.DBContext) error { | |||||
| image := models.Image{ | |||||
| Type: models.NORMAL_TYPE, | |||||
| CloudbrainType: params.CloudBrainType, | |||||
| UID: params.UID, | |||||
| IsPrivate: params.IsPrivate, | |||||
| Tag: params.ImageTag, | |||||
| Description: params.ImageDescription, | |||||
| Place: setting.Cloudbrain.ImageURLPrefix + params.ImageTag, | |||||
| } | |||||
| if err := models.CreateLocalImage(&image); err != nil { | |||||
| log.Error("Failed to insert image record.", err) | |||||
| return fmt.Errorf("CommitImage err: %s", res.String()) | |||||
| } | |||||
| if err := models.SaveImageTopics(image.ID, params.Topics...); err != nil { | |||||
| log.Error("Failed to insert image record.", err) | |||||
| return fmt.Errorf("CommitImage err: %s", res.String()) | |||||
| } | |||||
| return nil | |||||
| }) | |||||
| return err | |||||
| } | } | ||||
| func StopJob(jobID string) error { | func StopJob(jobID string) error { | ||||
| @@ -1,9 +1,10 @@ | |||||
| package setting | package setting | ||||
| type CloudbrainLoginConfig struct { | type CloudbrainLoginConfig struct { | ||||
| Username string | |||||
| Password string | |||||
| Host string | |||||
| Username string | |||||
| Password string | |||||
| Host string | |||||
| ImageURLPrefix string | |||||
| } | } | ||||
| var ( | var ( | ||||
| @@ -15,5 +16,6 @@ func GetCloudbrainConfig() CloudbrainLoginConfig { | |||||
| Cloudbrain.Username = cloudbrainSec.Key("USERNAME").MustString("") | Cloudbrain.Username = cloudbrainSec.Key("USERNAME").MustString("") | ||||
| Cloudbrain.Password = cloudbrainSec.Key("PASSWORD").MustString("") | Cloudbrain.Password = cloudbrainSec.Key("PASSWORD").MustString("") | ||||
| Cloudbrain.Host = cloudbrainSec.Key("REST_SERVER_HOST").MustString("") | Cloudbrain.Host = cloudbrainSec.Key("REST_SERVER_HOST").MustString("") | ||||
| Cloudbrain.ImageURLPrefix = cloudbrainSec.Key("IMAGE_URL_PREFIX").MustString("") | |||||
| return Cloudbrain | return Cloudbrain | ||||
| } | } | ||||
| @@ -878,7 +878,7 @@ readme_helper = Select a README file template. | |||||
| auto_init = Initialize Repository (Adds .gitignore, License and README) | auto_init = Initialize Repository (Adds .gitignore, License and README) | ||||
| create_repo = Create Repository | create_repo = Create Repository | ||||
| create_course = Publish Course | create_course = Publish Course | ||||
| failed_to_create_course=Fail to publish course, please try again later. | |||||
| failed_to_create_course=Failed to publish course, please try again later. | |||||
| default_branch = Default Branch | default_branch = Default Branch | ||||
| mirror_prune = Prune | mirror_prune = Prune | ||||
| mirror_prune_desc = Remove obsolete remote-tracking references | mirror_prune_desc = Remove obsolete remote-tracking references | ||||
| @@ -912,6 +912,8 @@ more=More | |||||
| gpu_type_all=All | gpu_type_all=All | ||||
| model_download=Model Download | model_download=Model Download | ||||
| submit_image=Submit Image | submit_image=Submit Image | ||||
| image_exist=Image name has been used, please use a new one. | |||||
| image_commit_fail=Failed to submit image, please try again later. | |||||
| download=Download | download=Download | ||||
| score=Score | score=Score | ||||
| @@ -948,7 +950,7 @@ total_count_get_error=Can not get the total page. | |||||
| last_update_time_error=Can not get the last updated time. | last_update_time_error=Can not get the last updated time. | ||||
| get_repo_stat_error=Can not get the statistics of the repository. | get_repo_stat_error=Can not get the statistics of the repository. | ||||
| get_repo_info_error=Can not get the information of the repository. | get_repo_info_error=Can not get the information of the repository. | ||||
| generate_statistic_file_error=Fail to generate file. | |||||
| generate_statistic_file_error=Failed to generate file. | |||||
| repo_stat_inspect=ProjectAnalysis | repo_stat_inspect=ProjectAnalysis | ||||
| all=All | all=All | ||||
| @@ -1097,7 +1099,7 @@ form.name_reserved = The repository name '%s' is reserved. | |||||
| form.course_name_reserved=The course name '%s' is reserved. | form.course_name_reserved=The course name '%s' is reserved. | ||||
| form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name. | form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name. | ||||
| form.course_name_pattern_not_allowed=The pattern '%s' is not allowed in a course name. | form.course_name_pattern_not_allowed=The pattern '%s' is not allowed in a course name. | ||||
| add_course_org_fail=Fail to add organization, please try again later. | |||||
| add_course_org_fail=Failed to add organization, please try again later. | |||||
| need_auth = Clone Authorization | need_auth = Clone Authorization | ||||
| migrate_type = Migration Type | migrate_type = Migration Type | ||||
| @@ -917,6 +917,8 @@ more=更多 | |||||
| gpu_type_all=全部 | gpu_type_all=全部 | ||||
| model_download=结果下载 | model_download=结果下载 | ||||
| submit_image=提交镜像 | submit_image=提交镜像 | ||||
| image_exist=镜像名称已被使用,请修改镜像名称。 | |||||
| image_commit_fail=提交镜像失败,请稍后再试。 | |||||
| download=模型下载 | download=模型下载 | ||||
| score=评分 | score=评分 | ||||
| @@ -2142,8 +2144,8 @@ branch.included=已包含 | |||||
| topic.manage_topics=管理主题 | topic.manage_topics=管理主题 | ||||
| topic.done=保存 | topic.done=保存 | ||||
| topic.count_prompt=您最多选择25个主题 | |||||
| topic.format_prompt=主题必须以中文、字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符 | |||||
| topic.count_prompt=您最多选择25个标签 | |||||
| topic.format_prompt=标签必须以中文、字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符 | |||||
| [org] | [org] | ||||
| org_name_holder=组织名称 | org_name_holder=组织名称 | ||||
| @@ -20,6 +20,7 @@ import ( | |||||
| const ( | const ( | ||||
| tplCloudBrains base.TplName = "admin/cloudbrain/list" | tplCloudBrains base.TplName = "admin/cloudbrain/list" | ||||
| tplImages base.TplName = "admin/cloudbrain/images" | |||||
| EXCEL_DATE_FORMAT = "20060102150405" | EXCEL_DATE_FORMAT = "20060102150405" | ||||
| CREATE_TIME_FORMAT = "2006/01/02 15:04:05" | CREATE_TIME_FORMAT = "2006/01/02 15:04:05" | ||||
| ) | ) | ||||
| @@ -107,6 +108,11 @@ func CloudBrains(ctx *context.Context) { | |||||
| } | } | ||||
| func Images(ctx *context.Context) { | |||||
| ctx.HTML(200, tplImages) | |||||
| } | |||||
| func DownloadCloudBrains(ctx *context.Context) { | func DownloadCloudBrains(ctx *context.Context) { | ||||
| page := 1 | page := 1 | ||||
| @@ -37,6 +37,8 @@ const ( | |||||
| tplCloudBrainBenchmarkIndex base.TplName = "repo/cloudbrain/benchmark/index" | tplCloudBrainBenchmarkIndex base.TplName = "repo/cloudbrain/benchmark/index" | ||||
| tplCloudBrainBenchmarkNew base.TplName = "repo/cloudbrain/benchmark/new" | tplCloudBrainBenchmarkNew base.TplName = "repo/cloudbrain/benchmark/new" | ||||
| tplCloudBrainBenchmarkShow base.TplName = "repo/cloudbrain/benchmark/show" | tplCloudBrainBenchmarkShow base.TplName = "repo/cloudbrain/benchmark/show" | ||||
| tplCloudBrainImageSubmit base.TplName = "repo/cloudbrain/image/submit" | |||||
| tplCloudBrainImageEdit base.TplName = "repo/cloudbrain/image/edit" | |||||
| ) | ) | ||||
| var ( | var ( | ||||
| @@ -447,26 +449,84 @@ func CloudBrainDebug(ctx *context.Context) { | |||||
| ctx.Redirect(debugUrl) | ctx.Redirect(debugUrl) | ||||
| } | } | ||||
| func CloudBrainCommitImageShow(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| ctx.Data["Type"] = ctx.Cloudbrain.Type | |||||
| ctx.HTML(200, tplCloudBrainImageSubmit) | |||||
| } | |||||
| func CloudBrainImageEdit(ctx *context.Context) { | |||||
| ctx.Data["PageIsImageEdit"] = true | |||||
| ctx.Data["PageFrom"] = ctx.Params(":from") | |||||
| var ID = ctx.Params(":id") | |||||
| id, err := strconv.ParseInt(ID, 10, 64) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByID failed:%v", err.Error()) | |||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||||
| image, err := models.GetImageByID(id) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByID failed:%v", err.Error()) | |||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||||
| } | |||||
| ctx.Data["Image"] = image | |||||
| ctx.HTML(http.StatusOK, tplCloudBrainImageSubmit) | |||||
| } | |||||
| } | |||||
| func CloudBrainImageEditPost(ctx *context.Context, form auth.EditImageCloudBrainForm) { | |||||
| } | |||||
| func CloudBrainImageDelete(ctx *context.Context) { | |||||
| } | |||||
| func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | ||||
| var topics = make([]string, 0) | |||||
| var topicsStr = strings.TrimSpace(form.Topics) | |||||
| if len(topicsStr) > 0 { | |||||
| topics = strings.Split(topicsStr, ",") | |||||
| } | |||||
| validTopics, invalidTopics := models.SanitizeAndValidateTopics(topics) | |||||
| if len(validTopics) > 25 { | |||||
| ctx.JSON(200, models.BaseErrorMessage(ctx.Tr("repo.topic.count_prompt"))) | |||||
| return | |||||
| } | |||||
| if len(invalidTopics) > 0 { | |||||
| ctx.JSON(200, models.BaseErrorMessage(ctx.Tr("repo.topic.format_prompt"))) | |||||
| return | |||||
| } | |||||
| err := cloudbrain.CommitImage(ctx.Cloudbrain.JobID, models.CommitImageParams{ | err := cloudbrain.CommitImage(ctx.Cloudbrain.JobID, models.CommitImageParams{ | ||||
| Ip: ctx.Cloudbrain.ContainerIp, | |||||
| TaskContainerId: ctx.Cloudbrain.ContainerID, | |||||
| ImageDescription: form.Description, | |||||
| ImageTag: form.Tag, | |||||
| CommitImageCloudBrainParams: models.CommitImageCloudBrainParams{ | |||||
| Ip: ctx.Cloudbrain.ContainerIp, | |||||
| TaskContainerId: ctx.Cloudbrain.ContainerID, | |||||
| ImageDescription: form.Description, | |||||
| ImageTag: form.Tag, | |||||
| }, | |||||
| IsPrivate: form.IsPrivate, | |||||
| CloudBrainType: form.Type, | |||||
| Topics: validTopics, | |||||
| UID: ctx.User.ID, | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"]) | log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"]) | ||||
| ctx.JSON(200, map[string]string{ | |||||
| "result_code": "-1", | |||||
| "error_msg": "CommitImage failed", | |||||
| }) | |||||
| if models.IsErrImageTagExist(err) { | |||||
| ctx.JSON(200, models.BaseErrorMessage(ctx.Tr("repo.image_exist"))) | |||||
| } else { | |||||
| ctx.JSON(200, models.BaseErrorMessage(ctx.Tr("repo.image_commit_fail"))) | |||||
| } | |||||
| return | return | ||||
| } | } | ||||
| ctx.JSON(200, map[string]string{ | |||||
| "result_code": "0", | |||||
| "error_msg": "", | |||||
| }) | |||||
| ctx.JSON(200, models.BaseOKMessage) | |||||
| } | } | ||||
| func CloudBrainStop(ctx *context.Context) { | func CloudBrainStop(ctx *context.Context) { | ||||
| @@ -330,6 +330,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| }) | }) | ||||
| m.Get("/images/public", repo.GetPublicImages) | m.Get("/images/public", repo.GetPublicImages) | ||||
| m.Get("/images/custom", repo.GetCustomImages) | m.Get("/images/custom", repo.GetCustomImages) | ||||
| m.Get("/images/star", repo.GetCustomImages) | |||||
| m.Get("/repos", routers.ExploreRepos) | m.Get("/repos", routers.ExploreRepos) | ||||
| m.Get("/datasets", routers.ExploreDatasets) | m.Get("/datasets", routers.ExploreDatasets) | ||||
| m.Get("/users", routers.ExploreUsers) | m.Get("/users", routers.ExploreUsers) | ||||
| @@ -523,6 +525,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("", admin.CloudBrains) | m.Get("", admin.CloudBrains) | ||||
| m.Get("/download", admin.DownloadCloudBrains) | m.Get("/download", admin.DownloadCloudBrains) | ||||
| }) | }) | ||||
| m.Group("/images", func() { | |||||
| m.Get("", admin.Images) | |||||
| }) | |||||
| m.Group("/^:configType(hooks|system-hooks)$", func() { | m.Group("/^:configType(hooks|system-hooks)$", func() { | ||||
| m.Get("", admin.DefaultOrSystemWebhooks) | m.Get("", admin.DefaultOrSystemWebhooks) | ||||
| @@ -970,7 +975,12 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/:username/:reponame", func() { | m.Group("/:username/:reponame", func() { | ||||
| m.Post("/topics", repo.TopicsPost) | m.Post("/topics", repo.TopicsPost) | ||||
| }, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin) | }, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin) | ||||
| m.Group("/image/:id", func() { | |||||
| m.Get("/:from", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageEdit) | |||||
| m.Post("", cloudbrain.AdminOrImageCreaterRight, bindIgnErr(auth.EditImageCloudBrainForm{}), repo.CloudBrainImageEditPost) | |||||
| m.Delete("", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageDelete) | |||||
| }) | |||||
| m.Group("/:username/:reponame", func() { | m.Group("/:username/:reponame", func() { | ||||
| m.Group("", func() { | m.Group("", func() { | ||||
| m.Get("/^:type(issues|pulls)$", repo.Issues) | m.Get("/^:type(issues|pulls)$", repo.Issues) | ||||
| @@ -1012,6 +1022,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/:id", func() { | m.Group("/:id", func() { | ||||
| m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow) | m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow) | ||||
| m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDebug) | m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDebug) | ||||
| m.Get("/commit_image", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainCommitImageShow) | |||||
| m.Post("/commit_image", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage) | m.Post("/commit_image", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage) | ||||
| m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop) | m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop) | ||||
| m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel) | m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel) | ||||