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
+               }
+       }
+}

Reply via email to