From 4793fae46b3d3bbf991e9b1abd9b5937ad68ff44 Mon Sep 17 00:00:00 2001 From: ychao_1983 Date: Wed, 30 Mar 2022 15:32:53 +0800 Subject: [PATCH] =?UTF-8?q?=E9=95=9C=E5=83=8F=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/cloudbrain.go | 10 +- models/cloudbrain_image.go | 415 +++++++++++++++++++++++++++++++ models/models.go | 2 + modules/auth/cloudbrain.go | 20 +- modules/cloudbrain/cloudbrain.go | 34 +++ modules/cloudbrain/resty.go | 35 ++- modules/setting/cloudbrain.go | 8 +- options/locale/locale_en-US.ini | 8 +- options/locale/locale_zh-CN.ini | 6 +- routers/admin/cloudbrains.go | 6 + routers/repo/cloudbrain.go | 84 ++++++- routers/routes/routes.go | 13 +- 12 files changed, 616 insertions(+), 25 deletions(-) create mode 100644 models/cloudbrain_image.go diff --git a/models/cloudbrain.go b/models/cloudbrain.go index 17761a1dc..12e9ee2b1 100755 --- a/models/cloudbrain.go +++ b/models/cloudbrain.go @@ -518,13 +518,21 @@ type PoolInfo struct { PoolType string `json:"pool_type"` } -type CommitImageParams struct { +type CommitImageCloudBrainParams struct { Ip string `json:"ip"` TaskContainerId string `json:"taskContainerId"` ImageTag string `json:"imageTag"` ImageDescription string `json:"imageDescription"` } +type CommitImageParams struct { + CommitImageCloudBrainParams + IsPrivate bool + Topics []string + CloudBrainType int + UID int64 +} + type CommitImageResult struct { Code string `json:"code"` Msg string `json:"msg"` diff --git a/models/cloudbrain_image.go b/models/cloudbrain_image.go new file mode 100644 index 000000000..a6ca35f29 --- /dev/null +++ b/models/cloudbrain_image.go @@ -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 + } + +} diff --git a/models/models.go b/models/models.go index 36527f78d..57137448d 100755 --- a/models/models.go +++ b/models/models.go @@ -131,6 +131,8 @@ func init() { new(Dataset), new(DatasetStar), new(Cloudbrain), + new(Image), + new(ImageStar), new(FileChunk), new(BlockChain), new(RecommendOrg), diff --git a/modules/auth/cloudbrain.go b/modules/auth/cloudbrain.go index 9949feddc..2f345e6c5 100755 --- a/modules/auth/cloudbrain.go +++ b/modules/auth/cloudbrain.go @@ -24,9 +24,27 @@ type CreateCloudBrainForm struct { type CommitImageCloudBrainForm struct { 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 { 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) +} diff --git a/modules/cloudbrain/cloudbrain.go b/modules/cloudbrain/cloudbrain.go index 9aae447b0..d10fe4e22 100755 --- a/modules/cloudbrain/cloudbrain.go +++ b/modules/cloudbrain/cloudbrain.go @@ -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) { 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 { dataActualPath := setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + diff --git a/modules/cloudbrain/resty.go b/modules/cloudbrain/resty.go index 46b7c991b..d3ce40191 100755 --- a/modules/cloudbrain/resty.go +++ b/modules/cloudbrain/resty.go @@ -210,6 +210,17 @@ func getQueryString(page int, size int, name string) string { } 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() client := getRestyClient() var result models.CommitImageResult @@ -220,7 +231,7 @@ sendjob: res, err := client.R(). SetHeader("Content-Type", "application/json"). SetAuthToken(TOKEN). - SetBody(params). + SetBody(params.CommitImageCloudBrainParams). SetResult(&result). Post(HOST + "/rest-server/api/v1/jobs/" + jobID + "/commitImage") @@ -238,7 +249,27 @@ sendjob: 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 { diff --git a/modules/setting/cloudbrain.go b/modules/setting/cloudbrain.go index c0ab3b275..2d80eea25 100755 --- a/modules/setting/cloudbrain.go +++ b/modules/setting/cloudbrain.go @@ -1,9 +1,10 @@ package setting type CloudbrainLoginConfig struct { - Username string - Password string - Host string + Username string + Password string + Host string + ImageURLPrefix string } var ( @@ -15,5 +16,6 @@ func GetCloudbrainConfig() CloudbrainLoginConfig { Cloudbrain.Username = cloudbrainSec.Key("USERNAME").MustString("") Cloudbrain.Password = cloudbrainSec.Key("PASSWORD").MustString("") Cloudbrain.Host = cloudbrainSec.Key("REST_SERVER_HOST").MustString("") + Cloudbrain.ImageURLPrefix = cloudbrainSec.Key("IMAGE_URL_PREFIX").MustString("") return Cloudbrain } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a990e9aee..10a105627 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -878,7 +878,7 @@ readme_helper = Select a README file template. auto_init = Initialize Repository (Adds .gitignore, License and README) create_repo = Create Repository 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 mirror_prune = Prune mirror_prune_desc = Remove obsolete remote-tracking references @@ -912,6 +912,8 @@ more=More gpu_type_all=All model_download=Model Download 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 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. get_repo_stat_error=Can not get the statistics 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 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.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. -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 migrate_type = Migration Type diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 4e73fbad5..c2f81e4dd 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -917,6 +917,8 @@ more=更多 gpu_type_all=全部 model_download=结果下载 submit_image=提交镜像 +image_exist=镜像名称已被使用,请修改镜像名称。 +image_commit_fail=提交镜像失败,请稍后再试。 download=模型下载 score=评分 @@ -2142,8 +2144,8 @@ branch.included=已包含 topic.manage_topics=管理主题 topic.done=保存 -topic.count_prompt=您最多选择25个主题 -topic.format_prompt=主题必须以中文、字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符 +topic.count_prompt=您最多选择25个标签 +topic.format_prompt=标签必须以中文、字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符 [org] org_name_holder=组织名称 diff --git a/routers/admin/cloudbrains.go b/routers/admin/cloudbrains.go index 6bbd534b9..86e81cb87 100644 --- a/routers/admin/cloudbrains.go +++ b/routers/admin/cloudbrains.go @@ -20,6 +20,7 @@ import ( const ( tplCloudBrains base.TplName = "admin/cloudbrain/list" + tplImages base.TplName = "admin/cloudbrain/images" EXCEL_DATE_FORMAT = "20060102150405" 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) { page := 1 diff --git a/routers/repo/cloudbrain.go b/routers/repo/cloudbrain.go index fc1dbfbd0..4d1b08418 100755 --- a/routers/repo/cloudbrain.go +++ b/routers/repo/cloudbrain.go @@ -37,6 +37,8 @@ const ( tplCloudBrainBenchmarkIndex base.TplName = "repo/cloudbrain/benchmark/index" tplCloudBrainBenchmarkNew base.TplName = "repo/cloudbrain/benchmark/new" tplCloudBrainBenchmarkShow base.TplName = "repo/cloudbrain/benchmark/show" + tplCloudBrainImageSubmit base.TplName = "repo/cloudbrain/image/submit" + tplCloudBrainImageEdit base.TplName = "repo/cloudbrain/image/edit" ) var ( @@ -447,26 +449,84 @@ func CloudBrainDebug(ctx *context.Context) { 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) { + + 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{ - 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 { 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 } - ctx.JSON(200, map[string]string{ - "result_code": "0", - "error_msg": "", - }) + ctx.JSON(200, models.BaseOKMessage) } func CloudBrainStop(ctx *context.Context) { diff --git a/routers/routes/routes.go b/routers/routes/routes.go index c1ff24216..f12ebbab8 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -330,6 +330,8 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Get("/images/public", repo.GetPublicImages) m.Get("/images/custom", repo.GetCustomImages) + m.Get("/images/star", repo.GetCustomImages) + m.Get("/repos", routers.ExploreRepos) m.Get("/datasets", routers.ExploreDatasets) m.Get("/users", routers.ExploreUsers) @@ -523,6 +525,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("", admin.CloudBrains) m.Get("/download", admin.DownloadCloudBrains) }) + m.Group("/images", func() { + m.Get("", admin.Images) + }) m.Group("/^:configType(hooks|system-hooks)$", func() { m.Get("", admin.DefaultOrSystemWebhooks) @@ -970,7 +975,12 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/:username/:reponame", func() { m.Post("/topics", repo.TopicsPost) }, 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("", func() { m.Get("/^:type(issues|pulls)$", repo.Issues) @@ -1012,6 +1022,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/:id", func() { m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow) 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("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop) m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel)