From 7f98c5831a13666c59dc50eb57348e7325bf86e0 Mon Sep 17 00:00:00 2001 From: Andrewpqc Date: Fri, 17 Aug 2018 16:05:00 +0800 Subject: [PATCH 1/7] chore: modify duplicate checker --- doc/apidoc.swagger.yaml | 22 +++++++++++-- handler/app.go | 20 ++++++------ handler/service.go | 69 +++++++++++++++++++++++++---------------- handler/user.go | 7 +++++ pkg/errno/code.go | 1 + pkg/mail/mail.go | 6 ++-- router/routers.go | 1 + 7 files changed, 83 insertions(+), 43 deletions(-) diff --git a/doc/apidoc.swagger.yaml b/doc/apidoc.swagger.yaml index 6924fc1..a4802c0 100644 --- a/doc/apidoc.swagger.yaml +++ b/doc/apidoc.swagger.yaml @@ -329,9 +329,9 @@ paths: required: true type: string responses: - 0: + 200: description: 检测项已存在于数据库中,即不可用 - 20002: + 404: description: 待检测项可用 /service: post: @@ -388,7 +388,23 @@ paths: description: offsize超范围或者app_id不存在 - + /service/duplicate: + get: + tags: + - "service" + summary: 检测service name是否可用 + description: 登录操作 + parameters: + - in: query + name: svcname + description: 待检测服务名 + required: true + type: string + responses: + 200: + description: 检测项已存在于数据库中,即不可用 + 404: + description: 待检测项可用 /service/{svc_name}: get: tags: diff --git a/handler/app.go b/handler/app.go index a96f6a1..9260581 100644 --- a/handler/app.go +++ b/handler/app.go @@ -1,13 +1,14 @@ package handler import ( + "fmt" + "time" "errors" "encoding/json" "github.com/kataras/iris" "github.com/muxiyun/Mae/model" "github.com/muxiyun/Mae/pkg/errno" "github.com/muxiyun/Mae/pkg/mail" - "time" ) //create a new app @@ -36,6 +37,7 @@ func GetApp(ctx iris.Context) { SendResponse(ctx, nil, app) } + //update the info of a app func UpdateApp(ctx iris.Context) { var newapp model.App @@ -84,18 +86,16 @@ func GetAppList(ctx iris.Context) { func AppNameDuplicateChecker(ctx iris.Context) { appname := ctx.URLParamDefault("appname", "") - if appname != "" { - app, err := model.GetAppByName(appname) - if err != nil { - SendResponse(ctx, errno.New(errno.ErrDatabase, err), - iris.Map{"message": app.AppName + " not exists"}) - return - } - SendResponse(ctx, nil, iris.Map{"message": appname + " exists"}) + app, _ := model.GetAppByName(appname) + if app.AppName == "" { + ctx.StatusCode(iris.StatusNotFound) + ctx.WriteString(fmt.Sprintf("app %s not exist", appname)) return } + ctx.StatusCode(iris.StatusOK) + ctx.WriteString(fmt.Sprintf("app %s already exist", appname)) + return - SendResponse(ctx, errno.New(errno.ErrAppNameNotProvide, errors.New("")), nil) } diff --git a/handler/service.go b/handler/service.go index 38ee89a..7f4f2dc 100644 --- a/handler/service.go +++ b/handler/service.go @@ -1,8 +1,8 @@ package handler import ( - "fmt" "encoding/json" + "fmt" "github.com/kataras/iris" "github.com/kataras/iris/core/errors" "github.com/muxiyun/Mae/model" @@ -86,18 +86,18 @@ func DeleteService(ctx iris.Context) { service_id, _ := ctx.Params().GetInt64("id") // get the current service object - service,err:=model.GetServiceByID(service_id) - if err!=nil{ - SendResponse(ctx,errno.New(errno.ErrDatabase,err),nil) + service, err := model.GetServiceByID(service_id) + if err != nil { + SendResponse(ctx, errno.New(errno.ErrDatabase, err), nil) return } // current service have active version - if service.CurrentVersion!=""{ - version:=&model.Version{} + if service.CurrentVersion != "" { + version := &model.Version{} d := model.DB.RWdb.Where("version_name = ?", service.CurrentVersion).Find(&version) - if d.Error!=nil{ - SendResponse(ctx,errno.New(errno.ErrDatabase,d.Error),nil) + if d.Error != nil { + SendResponse(ctx, errno.New(errno.ErrDatabase, d.Error), nil) return } @@ -105,17 +105,17 @@ func DeleteService(ctx iris.Context) { var version_config model.VersionConfig json.Unmarshal([]byte(version.VersionConfig), &version_config) - if err:=DeleteDeploymentAndServiceInCluster(version_config);err!=nil{ - SendResponse(ctx,errno.New(errno.ErrDeleteResourceInCluster,err),nil) + if err := DeleteDeploymentAndServiceInCluster(version_config); err != nil { + SendResponse(ctx, errno.New(errno.ErrDeleteResourceInCluster, err), nil) return } } //delete versions which belongs to current service - d:=model.DB.RWdb.Unscoped().Delete(model.Version{}, "svc_id = ?", service_id) - if d.Error!=nil{ - SendResponse(ctx,errno.New(errno.ErrDatabase,d.Error),nil) + d := model.DB.RWdb.Unscoped().Delete(model.Version{}, "svc_id = ?", service_id) + if d.Error != nil { + SendResponse(ctx, errno.New(errno.ErrDatabase, d.Error), nil) return } @@ -125,32 +125,31 @@ func DeleteService(ctx iris.Context) { return } - notification:=mail.NotificationEvent{ - Level:"Warning", - UserName:"Admin user", - Who:ctx.Values().GetString("current_user_name"), - Action:" delete ", - What:" service ["+ service.SvcName+"]", - When:time.Now().String(), + notification := mail.NotificationEvent{ + Level: "Warning", + UserName: "Admin user", + Who: ctx.Values().GetString("current_user_name"), + Action: " delete ", + What: " service [" + service.SvcName + "]", + When: time.Now().String(), } - receptions:=[]string{} + receptions := []string{} var adminUsers []model.User d = model.DB.RWdb.Where("role = ?", "admin").Find(&adminUsers) - if d.Error!=nil{ - SendResponse(ctx,errno.New(errno.ErrDatabase,d.Error),nil) + if d.Error != nil { + SendResponse(ctx, errno.New(errno.ErrDatabase, d.Error), nil) return } - for _,admin:=range adminUsers{ - receptions=append(receptions,admin.Email) + for _, admin := range adminUsers { + receptions = append(receptions, admin.Email) } - mail.SendNotificationEmail(notification,receptions) + mail.SendNotificationEmail(notification, receptions) SendResponse(ctx, nil, iris.Map{"id": service_id}) } - //get all services or services that belongs to an app func GetServiceList(ctx iris.Context) { limit := ctx.URLParamIntDefault("limit", 20) //how many if limit=0,default=20 @@ -180,3 +179,19 @@ func GetServiceList(ctx iris.Context) { } SendResponse(ctx, nil, iris.Map{"count": count, "svcs": svcs}) } + +//check whether a service name exist in db +func ServiceNameDuplicateChecker(ctx iris.Context) { + svcName := ctx.URLParamDefault("svcname", "") + + svc, _ := model.GetServiceByName(svcName) + if svc.SvcName == "" { + ctx.StatusCode(iris.StatusNotFound) + ctx.WriteString(fmt.Sprintf("service %s not exist", svcName)) + return + } + ctx.StatusCode(iris.StatusOK) + ctx.WriteString(fmt.Sprintf("service %s already exist", svc.SvcName)) + return + +} \ No newline at end of file diff --git a/handler/user.go b/handler/user.go index 6348d1d..3640083 100644 --- a/handler/user.go +++ b/handler/user.go @@ -214,6 +214,13 @@ func UserInfoDuplicateChecker(ctx iris.Context) { email := ctx.URLParamDefault("email", "") //检查用户名是否占用 if username != "" { + //不能使用roleAdmin,roleUser,roleAnonymous作为用户名 + if username=="roleAdmin"|| username=="roleUser"||username=="roleAnonymous"{ + ctx.StatusCode(iris.StatusOK) + ctx.WriteString(fmt.Sprintf("user %s already exist", username)) + return + } + user, _ := model.GetUserByName(username) if user.UserName != "" { ctx.StatusCode(iris.StatusOK) diff --git a/pkg/errno/code.go b/pkg/errno/code.go index 7a9cfc5..dd71f24 100644 --- a/pkg/errno/code.go +++ b/pkg/errno/code.go @@ -34,6 +34,7 @@ var ( ErrCreateApp = &Errno{Code: 20301, Message: "Error occurred while create App"} ErrGetApp = &Errno{Code: 20302, Message: "Error occurred while get app by app_name"} ErrAppNameNotProvide = &Errno{Code: 20303, Message: "AppName not provided"} + ErrServiceNameNotProvide =&Errno{Code:20304,Message: "svcName not provided"} //Service ServiceNameEmptyorAppIDTypeError = &Errno{Code: 20401, Message: "Error Service Name can't be empty and app_id should be int but not string "} diff --git a/pkg/mail/mail.go b/pkg/mail/mail.go index dd6f3fb..2fd8516 100644 --- a/pkg/mail/mail.go +++ b/pkg/mail/mail.go @@ -1,11 +1,11 @@ package mail import ( + "log" "bytes" - "gopkg.in/gomail.v2" - "html/template" "io/ioutil" - "log" + "html/template" + "gopkg.in/gomail.v2" ) type NotificationEvent struct { diff --git a/router/routers.go b/router/routers.go index dc53dea..9b9848d 100644 --- a/router/routers.go +++ b/router/routers.go @@ -67,6 +67,7 @@ func Load(app *iris.Application) *iris.Application { service_app.Delete("/{id:long}", handler.DeleteService) service_app.Get("/{svc_name:string}", handler.GetService) service_app.Get("", handler.GetServiceList) + service_app.Get("/duplicate", handler.ServiceNameDuplicateChecker) } version_app := app.Party("/api/v1.0/version") From 84bb17107a87b5bfcb825d71dd4fec58075554c3 Mon Sep 17 00:00:00 2001 From: Andrewpqc Date: Fri, 17 Aug 2018 16:10:52 +0800 Subject: [PATCH 2/7] fix: fix confirm link effective duration --- handler/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/user.go b/handler/user.go index 3640083..fa2bbd3 100644 --- a/handler/user.go +++ b/handler/user.go @@ -32,7 +32,7 @@ func getConfirmLink(ctx iris.Context, user model.User) (string, error) { tokenString, err := tk.GenJWToken(map[string]interface{}{ "username": user.UserName, "signTime": time.Now().Unix(), - "validdeltatime": 30, // 30 minutes + "validdeltatime": 30*60, // 30 minutes }) if err != nil { return "", err From 040386ccf25f610ced910cfd7e4d9cc23cc9a4f0 Mon Sep 17 00:00:00 2001 From: Andrewpqc Date: Sat, 18 Aug 2018 09:29:11 +0800 Subject: [PATCH 3/7] docs: fix --- doc/apidoc.swagger.yaml | 194 +++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 102 deletions(-) diff --git a/doc/apidoc.swagger.yaml b/doc/apidoc.swagger.yaml index a4802c0..0327bae 100644 --- a/doc/apidoc.swagger.yaml +++ b/doc/apidoc.swagger.yaml @@ -37,11 +37,11 @@ tags: paths: /token: get: - tags: + tags: - "auth" summary: 登录获取token - description: 用户名密码获取token  - parameters: + description: 用户名密码获取token  + parameters: - name: ex in: query type: integer @@ -64,14 +64,14 @@ paths: description: 需要用户名和密码登录,但是服务器端接收到token 20109: description: unauthorized(未提供Authorization请求头) - + /user: post: tags: - "user" summary: 创建用户 - description: username,password,email必选,role可选("user" or "admin"),默认为'user' - parameters: + description: username,password,email必选,role可选("user" or "admin"),默认为'user' + parameters: - in: body name: POST DATA description: json请求体 @@ -97,7 +97,7 @@ paths: - "user" summary: 获取用户列表 description: 管理员操作 - parameters: + parameters: - name: limit in: query type: integer @@ -119,7 +119,7 @@ paths: - "user" summary: 根据用户名获取单个用户信息 description: 携带token操作 - parameters: + parameters: - in: path name: username type: string @@ -132,14 +132,14 @@ paths: description: Forbidden 20002: description: 用户名不存在 - + /user/{id}: delete: tags: - "user" summary: 根据id删除用户 description: 携带token,且管理员才可操作 - parameters: + parameters: - in: path name: id type: integer @@ -152,13 +152,13 @@ paths: description: Forbidden 20002: description: 该id的用户不存在 - + put: - tags: + tags: - "user" summary: 根据id更新用户信息 description: 需登录操作,支持修改username,password,email - parameters: + parameters: - in: path name: id type: integer @@ -166,7 +166,7 @@ paths: required: true - in: body name: POST DATA - description: 包含需更新字段的对象 + description: 包含需更新字段的对象 required: true type: object schema: @@ -191,7 +191,7 @@ paths: - "user" summary: 检查邮箱或用户名是否已被占用 description: 注册新用户时,动态的检测用户输入的用户名,邮箱是否可用。一次只可传username,email中的一个,若两个都传默认检测username是否存在 - parameters: + parameters: - in: query name: username type: string @@ -212,19 +212,19 @@ paths: tags: - "app" summary: 创建一个应用 - description: 登录操作   + description: 登录操作   parameters: - - in: body - name: POST DATA - description: 包含一个应用数据的对象 - required: true - type: object - schema: - properties: - app_name: - type: string - app_desc: - type: string + - in: body + name: POST DATA + description: 包含一个应用数据的对象 + required: true + type: object + schema: + properties: + app_name: + type: string + app_desc: + type: string responses: 0: description: OK @@ -236,16 +236,16 @@ paths: summary: 获取app列表 description: 登录操作 parameters: - - in: query - name: limit - description: 一次获取app个数,可选,默认20 - required: false - type: integer - - in: query - name: offsize - description: 从哪里开始获取,可选,默认从0开始,即第一条开始 - required: false - type: integer + - in: query + name: limit + description: 一次获取app个数,可选,默认20 + required: false + type: integer + - in: query + name: offsize + description: 从哪里开始获取,可选,默认从0开始,即第一条开始 + required: false + type: integer responses: 0: description: OK @@ -277,22 +277,22 @@ paths: summary: 根据id更新app信息 description: 登录操作,可更新的字段为app_name,app_desc,更新则传,不更新不传 parameters: - - in: path - name: id - type: integer - description: 需更新的app的id - required: true - - in: body - name: POST DATA - description: 包含更新字段的对象 - required: true - type: object - schema: - properties: - app_name: - type: string - app_desc: - type: string + - in: path + name: id + type: integer + description: 需更新的app的id + required: true + - in: body + name: POST DATA + description: 包含更新字段的对象 + required: true + type: object + schema: + properties: + app_name: + type: string + app_desc: + type: string responses: 0: description: OK @@ -321,7 +321,7 @@ paths: tags: - "app" summary: 检测appname是否可用 - description: 登录操作 + description: 登录操作 parameters: - in: query name: appname @@ -340,19 +340,19 @@ paths: summary: 创建service description: 登录操作 parameters: - - in: body - name: POST DATA - description: 包含创建service的字段信息的对象 - required: true - type: object - schema: - properties: - app_id: - type: string - svc_name: - type: string - svc_desc: - type: string + - in: body + name: POST DATA + description: 包含创建service的字段信息的对象 + required: true + type: object + schema: + properties: + app_id: + type: string + svc_name: + type: string + svc_desc: + type: string responses: 0: description: OK @@ -367,7 +367,7 @@ paths: - in: query name: offsize type: integer - description: 从哪里开始获取,默认为从第一条开始获取 + description: 从哪里开始获取,默认为从第一条开始获取 required: false - in: query name: limit @@ -393,7 +393,7 @@ paths: tags: - "service" summary: 检测service name是否可用 - description: 登录操作 + description: 登录操作 parameters: - in: query name: svcname @@ -414,7 +414,7 @@ paths: parameters: - in: path name: svc_name - description: 服务名称 + description: 服务名称 required: true type: string responses: @@ -437,7 +437,7 @@ paths: required: true - in: body name: POST DATA - description: 包含更新字段的对象 + description: 包含更新字段的对象 required: true type: object schema: @@ -452,7 +452,7 @@ paths: 0: description: OK 20002: - description: id不存在或svc_name重复 + description: id不存在或svc_name重复 delete: tags: - "service" @@ -547,13 +547,13 @@ paths: 20507: description: 在集群中删除资源出错 - - - /version/unapply: + + + /version/unapply: get: tags: - "version" - summary: 取消某一个正在应用的版本 + summary: 取消某一个正在应用的版本 description: 登录操作。如果想直接停止某一个service的话,可以对该service的当前活跃version进行unapply操作,该操作在集群中删除该版本对应的cluster资源(该version的数据库记录仍然存在,下一次仍然可以被apply) parameters: - in: query @@ -592,7 +592,7 @@ paths: tags: - "version" summary: 删除版本 - description: 管理员操作。如果需删除的版本是active的,那么将先删除该版本对应的cluster资源,然后删除删除该版本的数据库记录。如果非active,则只删除该version的数据库记录。 + description: 管理员操作。如果需删除的版本是active的,那么将先删除该版本对应的cluster资源,然后删除删除该版本的数据库记录。如果非active,则只删除该version的数据库记录。 parameters: - in: path name: id @@ -612,7 +612,7 @@ paths: tags: - "ns" summary: 创建namespace - description: 登录操作 + description: 登录操作 parameters: - in: path name: ns @@ -647,13 +647,13 @@ paths: tags: - "ns" summary: 获取namespace列表 - description: 登录操作。普通用户无法获取kube-system,kube-public,default中的pod信息。管理员用户可以获取所有命名空间的pod + description: 登录操作。普通用户无法获取kube-system,kube-public,default中的pod信息。管理员用户可以获取所有命名空间的pod responses: 0: description: OK 20201: description: 命名空间获取出错 - + /api/v1.0/pod/{ns}: get: tags: @@ -677,7 +677,7 @@ paths: get: tags: - "log" - summary: 查询容器log + summary: 查询容器log description: 登录操作,普通用户无法获取kube-system,kube-public,default中的容器log。管理员用户可以获取所有命名空间的容器log parameters: - in: path @@ -700,14 +700,14 @@ paths: description: OK 403: description: Forbidden - /terminal/{ns}/{pod_name}/{container_name}: + /terminal/{ns}/{pod_name}/{container_name}: get: tags: - "terminal" summary: 与某一容器建立web terminal会话 description: 登录操作,普通用户无法获取kube-system,kube-public,default中的容器。管理员用户可以获取所有命名空间的容器log parameters: - - in: path + - in: path name: ns description: 命名空间名称 type: string @@ -732,7 +732,7 @@ paths: tags: - "sd" summary: 应用健康检查 - description: 无需验证 + description: 无需验证 responses: 200: description: OK @@ -743,19 +743,19 @@ paths: get: tags: - "sd" - summary: cup状态  + summary: cup状态  description: 管理员操作 responses: 200: description: OK - + /sd/disk: get: tags: - "sd" - summary: 磁盘状态 - description: 管理员操作 + summary: 磁盘状态 + description: 管理员操作 responses: 200: description: OK @@ -766,8 +766,8 @@ paths: get: tags: - "sd" - summary: 内存状态 - description: 管理员操作 + summary: 内存状态 + description: 管理员操作 responses: 200: description: OK @@ -775,16 +775,6 @@ paths: description: Forbidden - - - - - - - - - - definitions: VersionConf: type: object @@ -842,7 +832,7 @@ definitions: properties: cmd: type: "string" - + Env: type: "array" items: @@ -878,7 +868,7 @@ definitions: type: integer protocol: type: string - + Containers: type: "array" items: From 63328f4028e5d63dd5caebb1b1f0ebe0c0356b45 Mon Sep 17 00:00:00 2001 From: Andrewpqc Date: Sat, 18 Aug 2018 10:47:35 +0800 Subject: [PATCH 4/7] docs: update --- README.md | 62 +++++++++++++------ {model/db-uml => doc/DB-UML}/databaseUML.png | Bin {model/db-uml => doc/DB-UML}/domainUML.png | Bin doc/apidoc.markdown.md | 11 +++- test_utils.go | 2 +- 5 files changed, 55 insertions(+), 20 deletions(-) rename {model/db-uml => doc/DB-UML}/databaseUML.png (100%) rename {model/db-uml => doc/DB-UML}/domainUML.png (100%) diff --git a/README.md b/README.md index 62b7f22..e2dd712 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,47 @@ PaaS of Muxi-Studio. An easier way to manage Kubernetes cluser. Click [http://zxc0328.github.io/2017/05/27/mae/](http://zxc0328.github.io/2017/05/27/mae/) to view details. -TODO: -- [x] api design -- [x] domain UML & database UML -- [x] user system -- [x] casbin access control -- [x] application (abstract entity) -- [x] service (abstract entity) -- [x] version (abstract entity) -- [x] log query -- [x] web terminal -- [x] email notification for admin -NEXT: - -1.现在版本的切换采取的是删除原版本的资源之后创建新版本的资源,后面改为灰度发布 - -2.优化int类型的使用,重构部分代码 - -3.文档 +## Feature: +- [x] fast version switch and management +- [x] application management for microservices +- [x] casbin access control(RBAC with domains/tenants) +- [x] log query of sepcific container +- [x] web terminal of specific container +- [x] email confirm and email notification + +## Build +Clone the source code and cd to the root dir of this project and execute the command below to install the dependencies. +``` bash +$ glide install +``` +Users of mainland China may encounter some problem here. This project uses some dependencies that are blocked by the GWF. So you have to do terminal proxy configuration. How to config it? You can refer to this article:[https://andrewpqc.github.io/2018/04/30/let-the-terminal-penetrate-the-firewall.](https://andrewpqc.github.io/2018/04/30/let-the-terminal-penetrate-the-firewall) +you can also refer to [`glide mirror`](https://glide.readthedocs.io/en/latest/commands/#glide-mirror) to resove the problem. + +Before you run Mae, you firstly have to config it. How to config? + +Firstly, you have to get the admin's kubeconfig file, and make sure the name of this file is `admin.kubeconfig`(if not so, you may have to rename it). Then put the `admin.kubeconfig` file in the `conf` folder of this project. the `admin.kubeconfig` is the link between this program and the kubernets cluster. So it's really import. + +Secondly, You have to edit `conf/config.yaml` to config the mysql database connection information, the listen address and other configurable options. There are a lot of annotations in the config file, so you can view that to know more. + +After you have finished the config part, you can build and run it by typing the following command in you shell. +``` bash +$ go build && ./Mae +``` +Then, you can check `/api/v1.0/sd/health` to see whether it work properly or not. + +## Test +After you have finished the config part, you can just to use the following command to run the whole test. +``` bash +$ go test -v -cover=true +``` +But we don't to suggest you to do so. In order to prevent the rapid consumption of cluster resources and the interaction between test cases, we recommend that you run the test cases one by one in you integrated development environment(suggest [Goland](https://www.jetbrains.com/go/)). + +## Next: + +1. Grayscale release +2. Optimize the use of int types +3. Optimize the organization of the code +4. Further improve the documentation + +## Thanks +Thanks for developers of kubernetes,client-go,iris and mysql. \ No newline at end of file diff --git a/model/db-uml/databaseUML.png b/doc/DB-UML/databaseUML.png similarity index 100% rename from model/db-uml/databaseUML.png rename to doc/DB-UML/databaseUML.png diff --git a/model/db-uml/domainUML.png b/doc/DB-UML/domainUML.png similarity index 100% rename from model/db-uml/domainUML.png rename to doc/DB-UML/domainUML.png diff --git a/doc/apidoc.markdown.md b/doc/apidoc.markdown.md index b5419cd..9545d6a 100644 --- a/doc/apidoc.markdown.md +++ b/doc/apidoc.markdown.md @@ -1,16 +1,25 @@ ## Mae维护文档 ### 关于用户认证 -1.Mae认证采取Basic认证的方式 +Mae认证采取Basic Auth认证的方式,首先携带 `Authorization:base64.encode(username:password)`请求头到/api/v1.0/token取到token。之后需要登录的操作携带` Authorization:base64.encode(token:)`请求头即可以。 + +用户注册要求提供的邮箱真实有效,注册完毕之后,系统会发送一封邮件验证邮箱是否真实。验证的handler还会赋予该用户对应的权限(user/admin)。就是说,如果用户没有点击验证邮件中的链接,该用户的绝大部分操作都会被Forbidden. ### 关于权限管理 +Mae采用casbin的RBAC with domains/tenants 的访问控制模型。系统中所有的操作被分为三类:`roleAdmin`,`roleUser`,`roleAnonymous`。这三种类型也被称为三种角色(role)或者三个组(group). +一个操作是这样定义的`username,` + ### 关于错误码管理 ### 创建Version注意事项 ### 关于app,service的删除 +系统中实体之间的逻辑关系大致是这样的。系统中可以创建多个app(应用),一个应用之下有一个或多个service(服务),一个服务则有一个或者多个version(版本)。每一个版本其实就是一个在数据库中的用来在集群中创建deployment和service的配置文件的记录(序列化之后存成一个字段)。每一个版本有两个状态activa和unactive,active表明当前版本在集群中对应有资源,unactive则表明当前版本在集群中不存在资源。同时灭一个service也会有一个字段用来记录当前service对应的active版本是哪一个(一般来讲,一个service只有一个版本是active的)。 +由于app,service,version这些抽象实体之间是一种树状的结构,所以在删除时采取的是级联删除的方式 ### 关于邮件通知系统 +当系统中发生向删除app,service对象这样的操作时,会发送邮件通知所有的管理员用户 ### 关于namespace,deployment name,service naem的名称问题 +namespace,deloyment name,service name这些对象的命名必须采取与Kubernetes对象的命名相同的规则 \ No newline at end of file diff --git a/test_utils.go b/test_utils.go index 644dd5e..5b20612 100644 --- a/test_utils.go +++ b/test_utils.go @@ -8,7 +8,7 @@ import ( "github.com/muxiyun/Mae/handler" "strings" - "fmt" + // "fmt" ) type token struct { From f2d4ce88fe65d728f0982f6f932ab66443d9d0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=B6=85?= Date: Sat, 18 Aug 2018 10:52:56 +0800 Subject: [PATCH 5/7] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e2dd712..c7266bf 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Click [http://zxc0328.github.io/2017/05/27/mae/](http://zxc0328.github.io/2017/0 ## Feature: -- [x] fast version switch and management +- [x] fast and sliding version switch and management - [x] application management for microservices - [x] casbin access control(RBAC with domains/tenants) - [x] log query of sepcific container @@ -38,7 +38,7 @@ After you have finished the config part, you can just to use the following comma ``` bash $ go test -v -cover=true ``` -But we don't to suggest you to do so. In order to prevent the rapid consumption of cluster resources and the interaction between test cases, we recommend that you run the test cases one by one in you integrated development environment(suggest [Goland](https://www.jetbrains.com/go/)). +But we don't suggest you to do so. In order to prevent the rapid consumption of cluster resources and the interaction between test cases, we recommend that you run the test cases one by one in you integrated development environment(suggest [Goland](https://www.jetbrains.com/go/)). ## Next: @@ -48,4 +48,4 @@ But we don't to suggest you to do so. In order to prevent the rapid consumption 4. Further improve the documentation ## Thanks -Thanks for developers of kubernetes,client-go,iris and mysql. \ No newline at end of file +Thanks for developers of kubernetes,client-go,iris and mysql. From fb4dfda65292fcb2c1361c53ae55f4287d8211a3 Mon Sep 17 00:00:00 2001 From: Andrewpqc Date: Sat, 18 Aug 2018 12:23:56 +0800 Subject: [PATCH 6/7] docs: add logo --- README.md | 18 +++++++++++++++++- logo.go | 23 +++++++++++++++++++++++ main.go | 10 ++++++---- 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 logo.go diff --git a/README.md b/README.md index e2dd712..f046680 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,21 @@ # [muxi application engine(Mae)](https://github.com/muxiyun/Mae/tree/master) +``` + _ _ _ + __(.)< __(.)> __(.)= + \___) \___) \___) + _ _ _ + __(.)< __(.)> __(.)= + \___) \___) \___) + _ _ _ + __(.)< __(.)> __(.)= + \___) \___) \___) + _ _ _ + __(.)< __(.)> __(.)= + \___) \___) \___) + +``` + PaaS of Muxi-Studio. An easier way to manage Kubernetes cluser. Click [http://zxc0328.github.io/2017/05/27/mae/](http://zxc0328.github.io/2017/05/27/mae/) to view details. @@ -23,7 +39,7 @@ you can also refer to [`glide mirror`](https://glide.readthedocs.io/en/latest/co Before you run Mae, you firstly have to config it. How to config? -Firstly, you have to get the admin's kubeconfig file, and make sure the name of this file is `admin.kubeconfig`(if not so, you may have to rename it). Then put the `admin.kubeconfig` file in the `conf` folder of this project. the `admin.kubeconfig` is the link between this program and the kubernets cluster. So it's really import. +Firstly, you have to get the admin's kubeconfig file, and make sure the name of this file is `admin.kubeconfig`(if not so, you may have to rename it). Then put the `admin.kubeconfig` file in the `conf` folder of this project. the `admin.kubeconfig` is the link between this program and the kubernets cluster. So it's really important. Secondly, You have to edit `conf/config.yaml` to config the mysql database connection information, the listen address and other configurable options. There are a lot of annotations in the config file, so you can view that to know more. diff --git a/logo.go b/logo.go new file mode 100644 index 0000000..d761859 --- /dev/null +++ b/logo.go @@ -0,0 +1,23 @@ +package main + +import "fmt" + +func printLogo(){ + logo:=` + _ _ _ + __(.)< __(.)> __(.)= + \___) \___) \___) + _ _ _ Muxi application engine + __(.)< __(.)> __(.)= Copyright ©2018 MuxiStudio,MIT LICENSE. + \___) \___) \___) + _ _ _ + __(.)< __(.)> __(.)= + \___) \___) \___) + _ _ _ + __(.)< __(.)> __(.)= + \___) \___) \___) + + ` + +fmt.Println(logo) +} \ No newline at end of file diff --git a/main.go b/main.go index 50883f9..9ca8892 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "time" "errors" "net/http" @@ -64,17 +63,19 @@ func newApp() *iris.Application { func main() { + printLogo() + app := newApp() // start the mail service daemon go func() { - d := gomail.NewDialer("smtp.qq.com", 25, "3480437308@qq.com", "iifwjwzfjxvxchig") + + d := gomail.NewDialer(viper.GetString("mail.host"), viper.GetInt("mail.port"), viper.GetString("mail.username"), viper.GetString("mail.password")) var s gomail.SendCloser var err error open := false for { - fmt.Println("mail...") select { case m, ok := <-mail.Ms.Ch: if !ok { @@ -91,7 +92,8 @@ func main() { } // Close the connection to the SMTP server if no email was sent in // the last 30 seconds. - case <-time.After(30 * time.Second): + + case <-time.After(time.Duration(viper.GetInt("mail.maxFreeTime")) * time.Second): if open { if err := s.Close(); err != nil { panic(err) From 00076efc62c5d7f4b6c8d2f6b71ffc8d50c3d88f Mon Sep 17 00:00:00 2001 From: Andrewpqc Date: Sat, 18 Aug 2018 12:26:33 +0800 Subject: [PATCH 7/7] feat: turn many parameters into configurable --- conf/config.yaml | 23 ++++++++++++++++++++++- handler/token.go | 3 ++- handler/user.go | 3 ++- launch.sh | 3 --- pkg/k8sclient/k8sclient.go | 4 ---- pkg/mail/mail.go | 6 +++--- 6 files changed, 29 insertions(+), 13 deletions(-) delete mode 100755 launch.sh diff --git a/conf/config.yaml b/conf/config.yaml index b08837b..bd1d4e0 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -3,9 +3,10 @@ addr: :8080 # HTTP绑定端口 name: mae_apiserver # API Server的名字 url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port max_ping_count: 10 # pingServer函数try的次数 -jwt_secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5 +jwt_secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5 #46字符的随机字符串 casbinmodel: conf/casbinmodel.conf kubeconfig: conf/admin.kubeconfig +tokenEffectiveTime: 60 #token默认有效期时长,默认60分钟,请求时可以传参改变改值 tls: addr: :8081 cert: conf/example.com+4.pem @@ -20,7 +21,27 @@ log: log_rotate_size: 1 log_backup_count: 7 db: + #数据库名,跑之前提前建好 name: mae + #数据库地址 addr: 127.0.0.1:3306 + #用户名 username: root + #密码 password: pqc19960320 + +mail: + #邮件服务器地址 + host: smtp.qq.com + #邮件服务端口 + port: 25 + username: 3480437308@qq.com  + senderNickName: Mae notification robot  + password: iifwjwzfjxvxchig + #与邮件服务器连接的最大时长,默认30秒,即30秒之内没有需要发送的邮件则断开与邮件服务器的连接 + maxFreeTime: 30 + #邮件通道缓冲区大小,默认20 + chanCache: 20 + #认证邮件中的链接的有效期时长,默认30分钟之后认证邮件过期 + confirmTime: 30 + diff --git a/handler/token.go b/handler/token.go index b5668f2..394233f 100644 --- a/handler/token.go +++ b/handler/token.go @@ -7,10 +7,11 @@ import ( "github.com/kataras/iris" "github.com/muxiyun/Mae/pkg/errno" "github.com/muxiyun/Mae/pkg/token" + "github.com/spf13/viper" ) func SignToken(ctx iris.Context) { - validdeltatime := ctx.URLParamInt64Default("ex", 60*60) //validity period,default a hour + validdeltatime := ctx.URLParamInt64Default("ex", viper.GetInt64("tokenEffectiveTime")*60) current_user_name := ctx.Values().GetString("current_user_name") if current_user_name == "" { diff --git a/handler/user.go b/handler/user.go index fa2bbd3..5b122f6 100644 --- a/handler/user.go +++ b/handler/user.go @@ -12,6 +12,7 @@ import ( "github.com/muxiyun/Mae/pkg/errno" "github.com/muxiyun/Mae/pkg/mail" "github.com/muxiyun/Mae/pkg/token" + "github.com/spf13/viper" ) //获取验证链接 @@ -32,7 +33,7 @@ func getConfirmLink(ctx iris.Context, user model.User) (string, error) { tokenString, err := tk.GenJWToken(map[string]interface{}{ "username": user.UserName, "signTime": time.Now().Unix(), - "validdeltatime": 30*60, // 30 minutes + "validdeltatime": viper.GetInt("mail.confirmTime")*60, }) if err != nil { return "", err diff --git a/launch.sh b/launch.sh deleted file mode 100755 index 9e00318..0000000 --- a/launch.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -go build && ./Mae --addpolicy && ./Mae \ No newline at end of file diff --git a/pkg/k8sclient/k8sclient.go b/pkg/k8sclient/k8sclient.go index fd33f81..a56751c 100644 --- a/pkg/k8sclient/k8sclient.go +++ b/pkg/k8sclient/k8sclient.go @@ -1,7 +1,6 @@ package k8sclient import ( - "fmt" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -24,14 +23,12 @@ func init() { if err != nil { panic(err.Error()) } - fmt.Println("k8s client config success") //get clientset ClientSet, err = kubernetes.NewForConfig(Config) if err != nil { panic(err.Error()) } - fmt.Println("get clientset success") groupversion := schema.GroupVersion{ Group: "", @@ -47,6 +44,5 @@ func init() { if err != nil { panic(err.Error()) } - fmt.Println("get restclient success") } diff --git a/pkg/mail/mail.go b/pkg/mail/mail.go index 2fd8516..2c5fd58 100644 --- a/pkg/mail/mail.go +++ b/pkg/mail/mail.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "html/template" "gopkg.in/gomail.v2" + "github.com/spf13/viper" ) type NotificationEvent struct { @@ -33,12 +34,11 @@ var Ms MailService func Setup() { // init Ch - Ms.Ch = make(chan *gomail.Message, 20) + Ms.Ch = make(chan *gomail.Message, viper.GetInt("mail.chanCache")) // init Msg Ms.Msg = gomail.NewMessage() - Ms.Msg.SetHeader("From", Ms.Msg.FormatAddress("3480437308@qq.com", "Mae Notification Robot")) - //Ms.Msg.SetAddressHeader("Cc", "3480437308@qq.com", "Andrewpqc") + Ms.Msg.SetHeader("From", Ms.Msg.FormatAddress(viper.GetString("mail.username"), viper.GetString("mail.senderNickName"))) Ms.Msg.SetHeader("Subject", "Notification from Mae") notification, err := ioutil.ReadFile("./pkg/mail/notification.tpl")