This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new 8e21efc add upstream api (#280)
8e21efc is described below
commit 8e21efc59fb78c6c0c9202539db29dddf71bf87e
Author: kv <[email protected]>
AuthorDate: Wed Jun 24 19:32:28 2020 +0800
add upstream api (#280)
* add cd action
* add upstream api
* remove branch cd in github workflow
* remove pr phase in cd action
---
.github/workflows/api_cd.yml | 6 +-
api/errno/error.go | 9 ++
api/main.go | 1 +
api/route/upstream.go | 205 +++++++++++++++++++++++++++++++++++++
api/script/db/schema.sql | 14 ++-
api/service/route.go | 12 ++-
api/service/upstream.go | 237 +++++++++++++++++++++++++++++++++++++++++++
7 files changed, 473 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/api_cd.yml b/.github/workflows/api_cd.yml
index 528d8ad..98ce3bc 100644
--- a/.github/workflows/api_cd.yml
+++ b/.github/workflows/api_cd.yml
@@ -4,10 +4,6 @@ on:
push:
branches:
- master
- - cd
- pull_request:
- branches:
- - master
jobs:
build:
@@ -24,4 +20,4 @@ jobs:
- run: |
cd ./api
docker build . -t apisixacr.azurecr.cn/manager-api:${{ github.sha }}
- docker push apisixacr.azurecr.cn/manager-api:${{ github.sha }}
\ No newline at end of file
+ docker push apisixacr.azurecr.cn/manager-api:${{ github.sha }}
diff --git a/api/errno/error.go b/api/errno/error.go
index 20b7e24..2247541 100644
--- a/api/errno/error.go
+++ b/api/errno/error.go
@@ -49,6 +49,15 @@ var (
// 03 plugin module
ApisixPluginListError = Message{"010301", "List APISIX plugins
failed: %s"}
ApisixPluginSchemaError = Message{"010301", "Find APISIX plugin schema
failed: %s"}
+
+ // 06 upstream
+ UpstreamRequestError = Message{"010601", "upstream request parameters
are abnormal: %s"}
+ UpstreamTransError = Message{"010602", "upstream parameter conversion
is abnormal: %s"}
+ DBUpstreamError = Message{"010603", "upstream storage failure: %s"}
+ ApisixUpstreamCreateError = Message{"010604", "apisix upstream
create failure: %s"}
+ ApisixUpstreamUpdateError = Message{"010605", "apisix upstream
update failure: %s"}
+ ApisixUpstreamDeleteError = Message{"010606", "apisix upstream
delete failure: %s"}
+ DBUpstreamDeleteError = Message{"010607", "upstream delete
failure: %s"}
)
type ManagerError struct {
diff --git a/api/main.go b/api/main.go
index 3d756dd..d792e67 100644
--- a/api/main.go
+++ b/api/main.go
@@ -43,6 +43,7 @@ func setUpRouter() *gin.Engine {
route.AppendRoute(r)
route.AppendSsl(r)
route.AppendPlugin(r)
+ route.AppendUpstream(r)
pprof.Register(r)
diff --git a/api/route/upstream.go b/api/route/upstream.go
new file mode 100644
index 0000000..8330fbc
--- /dev/null
+++ b/api/route/upstream.go
@@ -0,0 +1,205 @@
+package route
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apisix/manager-api/conf"
+ "github.com/apisix/manager-api/errno"
+ "github.com/apisix/manager-api/service"
+ "github.com/gin-gonic/gin"
+ "github.com/satori/go.uuid"
+ "net/http"
+ "strconv"
+)
+
+func AppendUpstream(r *gin.Engine) *gin.Engine {
+ r.POST("/apisix/admin/upstreams", createUpstream)
+ r.GET("/apisix/admin/upstreams/:uid", findUpstream)
+ r.GET("/apisix/admin/upstreams", listUpstream)
+ r.PUT("/apisix/admin/upstreams/:uid", updateUpstream)
+ r.DELETE("/apisix/admin/upstreams/:uid", deleteUpstream)
+ return r
+}
+
+func createUpstream(c *gin.Context) {
+ u4 := uuid.NewV4()
+ uid := u4.String()
+ // todo 参数校验
+ param, exist := c.Get("requestBody")
+ if !exist || len(param.([]byte)) < 1 {
+ e := errno.FromMessage(errno.RouteRequestError, "upstream
create with no post data")
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+ return
+ }
+ // trans params
+ ur := &service.UpstreamRequest{}
+ if err := ur.Parse(param); err != nil {
+ e := errno.FromMessage(errno.UpstreamRequestError, err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+ return
+ }
+ ur.Id = uid
+ fmt.Println(ur)
+ if aur, err := ur.Parse2Apisix(); err != nil {
+ e := errno.FromMessage(errno.UpstreamTransError, err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+ return
+ } else {
+ // apisix
+ if resp, err := aur.Create(); err != nil {
+ e :=
errno.FromMessage(errno.ApisixUpstreamCreateError, err.Error())
+ logger.Error(e.Msg)
+
c.AbortWithStatusJSON(http.StatusInternalServerError, e.Response())
+ return
+ } else {
+ // mysql
+ fmt.Println(resp.UNode.UValue.Id)
+ fmt.Println(resp.UNode.UValue.Upstream.Nodes)
+ if ud, err := service.Trans2UpstreamDao(resp, ur); err
!= nil {
+
c.AbortWithStatusJSON(http.StatusInternalServerError, err.Response())
+ return
+ } else {
+ if err := conf.DB().Create(ud).Error; err !=
nil {
+ e :=
errno.FromMessage(errno.DBUpstreamError, err.Error())
+ logger.Error(e.Msg)
+
c.AbortWithStatusJSON(http.StatusInternalServerError, e.Response())
+ return
+ }
+ }
+ }
+ }
+ c.Data(http.StatusOK, service.ContentType, errno.Success())
+}
+
+func findUpstream(c *gin.Context) {
+ uid := c.Param("uid")
+ // find from apisix
+ aur := &service.ApisixUpstreamRequest{Id: uid}
+ if resp, err := aur.FindById(); err != nil {
+ e := errno.FromMessage(errno.UpstreamRequestError,
err.Error()+" upstream ID: "+uid)
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusInternalServerError,
e.Response())
+ return
+ } else {
+ if result, err := resp.Parse2Request(); err != nil {
+ e := errno.FromMessage(errno.UpstreamRequestError,
err.Error()+" upstream ID: "+uid)
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusInternalServerError,
e.Response())
+ return
+ } else {
+ resp, _ := json.Marshal(result)
+ c.Data(http.StatusOK, service.ContentType, resp)
+ }
+ }
+}
+
+func listUpstream(c *gin.Context) {
+ size, _ := strconv.Atoi(c.Query("size"))
+ page, _ := strconv.Atoi(c.Query("page"))
+ if size == 0 {
+ size = 10
+ }
+ db := conf.DB()
+ upstreamList := []service.UpstreamDao{}
+ var count int
+ if err := db.Order("update_time desc").Table("upstreams").Offset((page
- 1) * size).Limit(size).Find(&upstreamList).Count(&count).Error; err != nil {
+ e := errno.FromMessage(errno.RouteRequestError, err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusInternalServerError,
e.Response())
+ return
+ } else {
+ responseList := make([]*service.UpstreamResponse, 0)
+ for _, r := range upstreamList {
+ response, err := r.Parse2Response()
+ if err == nil {
+ responseList = append(responseList, response)
+ }
+ }
+ result := &service.ListResponse{Count: count, Data:
responseList}
+ resp, _ := json.Marshal(result)
+ c.Data(http.StatusOK, service.ContentType, resp)
+ }
+}
+func updateUpstream(c *gin.Context) {
+ uid := c.Param("uid")
+ // todo 参数校验
+ param, exist := c.Get("requestBody")
+ if !exist || len(param.([]byte)) < 1 {
+ e := errno.FromMessage(errno.RouteRequestError, "upstream
update with no post data")
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+ return
+ }
+ // trans params
+ ur := &service.UpstreamRequest{}
+ if err := ur.Parse(param); err != nil {
+ e := errno.FromMessage(errno.UpstreamRequestError, err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+ return
+ }
+ ur.Id = uid
+ fmt.Println(ur)
+ if aur, err := ur.Parse2Apisix(); err != nil {
+ e := errno.FromMessage(errno.UpstreamTransError, err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+ return
+ } else {
+ // apisix
+ if resp, err := aur.Update(); err != nil {
+ e := errno.FromMessage(errno.ApisixUpstreamUpdateError,
err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusInternalServerError,
e.Response())
+ return
+ } else {
+ // mysql
+ if ud, err := service.Trans2UpstreamDao(resp, ur); err
!= nil {
+
c.AbortWithStatusJSON(http.StatusInternalServerError, err.Response())
+ return
+ } else {
+ if err :=
conf.DB().Model(&service.UpstreamDao{}).Update(ud).Error; err != nil {
+ e :=
errno.FromMessage(errno.DBUpstreamError, err.Error())
+ logger.Error(e.Msg)
+
c.AbortWithStatusJSON(http.StatusInternalServerError, e.Response())
+ return
+ }
+ }
+ }
+ }
+ c.Data(http.StatusOK, service.ContentType, errno.Success())
+}
+
+func deleteUpstream(c *gin.Context) {
+ uid := c.Param("uid")
+ // 参数校验
+ upstream := &service.UpstreamDao{}
+ if err := conf.DB().Table("upstreams").Where("id=?",
uid).First(&upstream).Error; err != nil {
+ e := errno.FromMessage(errno.UpstreamRequestError,
err.Error()+" upstream ID: "+uid)
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusBadRequest, e.Response())
+ return
+ }
+ // delete from apisix
+ request := &service.ApisixUpstreamRequest{Id: uid}
+ if _, err := request.Delete(); err != nil {
+ e := errno.FromMessage(errno.ApisixUpstreamDeleteError,
err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusInternalServerError,
e.Response())
+ return
+ } else {
+ // delete from mysql
+ rd := &service.UpstreamDao{}
+ rd.ID = uuid.FromStringOrNil(uid)
+ if err := conf.DB().Delete(rd).Error; err != nil {
+ e := errno.FromMessage(errno.DBUpstreamDeleteError,
err.Error())
+ logger.Error(e.Msg)
+ c.AbortWithStatusJSON(http.StatusInternalServerError,
e.Response())
+ return
+ }
+ }
+ c.Data(http.StatusOK, service.ContentType, errno.Success())
+}
diff --git a/api/script/db/schema.sql b/api/script/db/schema.sql
index 69f088c..300315f 100644
--- a/api/script/db/schema.sql
+++ b/api/script/db/schema.sql
@@ -8,7 +8,7 @@ CREATE TABLE `routes` (
`hosts` text,
`uris` text,
`upstream_nodes` text,
- `upstream_id` varchar(32) , -- fk
+ `upstream_id` varchar(64) , -- fk
`priority` int NOT NULL DEFAULT 0,
`state` int NOT NULL DEFAULT 1, -- 1-normal 0-disable
`content` text,
@@ -28,4 +28,16 @@ CREATE TABLE `ssls` (
`create_time` bigint(20) unsigned NOT NULL,
`update_time` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`)
+) DEFAULT CHARSET=utf8;
+-- upstream
+CREATE TABLE `upstreams` (
+ `id` varchar(64) NOT NULL unique,
+ `name` varchar(200) NOT NULL unique, -- not support
+ `description` varchar(200) DEFAULT NULL,
+ `nodes` text,
+ `content` text,
+ `content_admin_api` text,
+ `create_time` bigint(20),
+ `update_time` bigint(20),
+ PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
\ No newline at end of file
diff --git a/api/service/route.go b/api/service/route.go
index a0ea933..ce518d4 100644
--- a/api/service/route.go
+++ b/api/service/route.go
@@ -68,11 +68,7 @@ func (arr *ApisixRouteRequest) Parse(r *RouteRequest) {
func (rd *Route) Parse(r *RouteRequest, arr *ApisixRouteRequest) error {
//rd.Name = arr.Name
rd.Description = arr.Desc
- // todo transfer
- rd.Hosts = ""
- rd.Uris = ""
- rd.UpstreamNodes = ""
- rd.UpstreamId = ""
+ rd.UpstreamId = r.UpstreamId
if content, err := json.Marshal(r); err != nil {
return err
} else {
@@ -170,6 +166,7 @@ type RouteRequest struct {
Redirect *Redirect `json:"redirect,omitempty"`
Vars [][]string `json:"vars,omitempty"`
Upstream *Upstream `json:"upstream,omitempty"`
+ UpstreamId string `json:"upstream_id,omitempty"`
UpstreamProtocol string
`json:"upstream_protocol,omitempty"`
UpstreamPath *UpstreamPath `json:"upstream_path,omitempty"`
UpstreamHeader map[string]string
`json:"upstream_header,omitempty"`
@@ -354,6 +351,7 @@ type ApisixRouteRequest struct {
Hosts []string `json:"hosts,omitempty"`
Vars [][]string `json:"vars,omitempty"`
Upstream *Upstream `json:"upstream,omitempty"`
+ UpstreamId string `json:"upstream_id,omitempty"`
Plugins map[string]interface{} `json:"plugins,omitempty"`
//Name string `json:"name"`
}
@@ -505,6 +503,8 @@ func ToApisixRequest(routeRequest *RouteRequest)
*ApisixRouteRequest {
} else {
arr.Plugins = nil
}
+ // upstreamId
+ arr.UpstreamId = routeRequest.UpstreamId
return arr
}
@@ -558,5 +558,7 @@ func ToRoute(routeRequest *RouteRequest,
rd.UpstreamNodes = string(nb)
}
}
+ // upstreamId
+ rd.UpstreamId = arr.UpstreamId
return rd, nil
}
diff --git a/api/service/upstream.go b/api/service/upstream.go
new file mode 100644
index 0000000..466045b
--- /dev/null
+++ b/api/service/upstream.go
@@ -0,0 +1,237 @@
+package service
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apisix/manager-api/conf"
+ "github.com/apisix/manager-api/errno"
+ "github.com/apisix/manager-api/utils"
+ "github.com/satori/go.uuid"
+)
+
+type UpstreamDao struct {
+ Base
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ Nodes string `json:"nodes"`
+ Content string `json:"content"`
+ ContentAdminApi string `json:"content_admin_api"`
+}
+
+func (UpstreamDao) TableName() string {
+ return "upstreams"
+}
+
+type ApisixUpstreamRequest struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"desc"`
+ Upstream
+}
+
+func (u *ApisixUpstreamRequest) toJson() []byte {
+ res, _ := json.Marshal(&u)
+ return res
+}
+
+type UpstreamRequest struct {
+ Id string `json:"id,omitempty"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Upstream
+}
+
+func (u *UpstreamRequest) toJson() []byte {
+ res, _ := json.Marshal(&u)
+ return res
+}
+
+func (r *UpstreamRequest) Parse(body interface{}) error {
+ if err := json.Unmarshal(body.([]byte), r); err != nil {
+ r = nil
+ return err
+ }
+ return nil
+}
+
+func (r *UpstreamRequest) Parse2Apisix() (*ApisixUpstreamRequest, error) {
+ aur := &ApisixUpstreamRequest{
+ Id: r.Id,
+ Name: r.Name,
+ Description: r.Description,
+ Upstream: r.Upstream,
+ }
+ return aur, nil
+}
+
+type UpstreamResponse struct {
+ Base
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ Upstream
+}
+
+func (u *UpstreamDao) Parse2Response() (*UpstreamResponse, error) {
+ // upstream
+ aur := &ApisixUpstreamResponse{}
+ if err := json.Unmarshal([]byte(u.ContentAdminApi), &aur); err != nil {
+ return nil, err
+ } else {
+ v := aur.UNode.UValue
+ result := &UpstreamResponse{
+ Name: v.Name,
+ Description: v.Description,
+ }
+ result.Base = u.Base
+ result.Upstream = Upstream{
+ UType: v.UType,
+ Timeout: v.Timeout,
+ Nodes: v.Nodes,
+ EnableWebsocket: v.EnableWebsocket,
+ }
+ return result, nil
+ }
+}
+
+type ApisixUpstreamResponse struct {
+ Action string `json:"action"`
+ UNode *UNode `json:"node"`
+}
+
+type UNode struct {
+ UValue UValue `json:"value"`
+ ModifiedIndex uint64 `json:"modifiedIndex"`
+}
+
+type UValue struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"desc,omitempty"`
+ Upstream
+}
+
+func (u *UValue) toJson() []byte {
+ res, _ := json.Marshal(&u)
+ return res
+}
+
+func (aur *ApisixUpstreamResponse) Parse2Request() (*UpstreamRequest, error) {
+ v := aur.UNode.UValue
+ result := &UpstreamRequest{
+ Id: v.Id,
+ Name: v.Name,
+ Description: v.Description,
+ Upstream: v.Upstream,
+ }
+ return result, nil
+}
+
+func Trans2UpstreamDao(resp *ApisixUpstreamResponse, r *UpstreamRequest)
(*UpstreamDao, *errno.ManagerError) {
+ ips := make([]string, 0)
+ nodes := r.Nodes
+ for k, _ := range nodes {
+ ips = append(ips, k)
+ }
+ if nb, err := json.Marshal(ips); err != nil {
+ e := errno.FromMessage(errno.DBUpstreamError, err.Error())
+ logger.Warn(e.Msg)
+ return nil, e
+ } else {
+ u := &UpstreamDao{
+ Name: r.Name,
+ Description: r.Description,
+ Nodes: string(nb),
+ }
+ // id
+ u.ID = uuid.FromStringOrNil(r.Id)
+ // content
+ if content, err := json.Marshal(r); err != nil {
+ e := errno.FromMessage(errno.DBUpstreamError,
err.Error())
+ return nil, e
+ } else {
+ u.Content = string(content)
+ }
+ // content_admin_api
+ if respStr, err := json.Marshal(resp); err != nil {
+ e := errno.FromMessage(errno.DBUpstreamError,
err.Error())
+ return nil, e
+ } else {
+ u.ContentAdminApi = string(respStr)
+ }
+ return u, nil
+ }
+}
+
+func (aur *ApisixUpstreamRequest) Create() (*ApisixUpstreamResponse, error) {
+ url := fmt.Sprintf("%s/upstreams/%s", conf.BaseUrl, aur.Id)
+ if b, err := json.Marshal(aur); err != nil {
+ return nil, err
+ } else {
+ fmt.Println(string(b))
+ if resp, err := utils.Put(url, b); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ var arresp ApisixUpstreamResponse
+ if err := json.Unmarshal(resp, &arresp); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ return &arresp, nil
+ }
+ }
+ }
+}
+
+func (aur *ApisixUpstreamRequest) Update() (*ApisixUpstreamResponse, error) {
+ url := fmt.Sprintf("%s/upstreams/%s", conf.BaseUrl, aur.Id)
+ if b, err := json.Marshal(aur); err != nil {
+ return nil, err
+ } else {
+ fmt.Println(string(b))
+ if resp, err := utils.Patch(url, b); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ var arresp ApisixUpstreamResponse
+ if err := json.Unmarshal(resp, &arresp); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ return &arresp, nil
+ }
+ }
+ }
+}
+
+func (arr *ApisixUpstreamRequest) FindById() (*ApisixUpstreamResponse, error) {
+ url := fmt.Sprintf("%s/upstreams/%s", conf.BaseUrl, arr.Id)
+ if resp, err := utils.Get(url); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ var arresp ApisixUpstreamResponse
+ if err := json.Unmarshal(resp, &arresp); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ return &arresp, nil
+ }
+ }
+}
+
+func (arr *ApisixUpstreamRequest) Delete() (*ApisixUpstreamResponse, error) {
+ url := fmt.Sprintf("%s/upstreams/%s", conf.BaseUrl, arr.Id)
+ if resp, err := utils.Delete(url); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ var arresp ApisixUpstreamResponse
+ if err := json.Unmarshal(resp, &arresp); err != nil {
+ logger.Error(err.Error())
+ return nil, err
+ } else {
+ return &arresp, nil
+ }
+ }
+}