| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package service |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "io/ioutil" |
| "os/exec" |
| "strings" |
| "time" |
| |
| "github.com/apisix/manager-api/conf" |
| "github.com/apisix/manager-api/errno" |
| "github.com/apisix/manager-api/log" |
| "github.com/apisix/manager-api/utils" |
| uuid "github.com/satori/go.uuid" |
| ) |
| |
| const ( |
| ContentType = "application/json" |
| HTTP = "http" |
| HTTPS = "https" |
| SCHEME = "scheme" |
| WEBSOCKET = "websocket" |
| REDIRECT = "redirect" |
| PROXY_REWRIETE = "proxy-rewrite" |
| UPATHTYPE_STATIC = "static" |
| UPATHTYPE_REGX = "regx" |
| UPATHTYPE_KEEP = "keep" |
| ) |
| |
| var logger = log.GetLogger() |
| |
| func (r *RouteRequest) Parse(body interface{}) error { |
| if err := json.Unmarshal(body.([]byte), r); err != nil { |
| r = nil |
| return err |
| } else { |
| if r.Uris == nil || len(r.Uris) < 1 { |
| r.Uris = []string{"/*"} |
| } |
| if len(strings.Trim(r.RouteGroupId, "")) > 0 { |
| routeGroup := &RouteGroupDao{} |
| if err, _ := routeGroup.FindRouteGroup(r.RouteGroupId); err != nil { |
| return err |
| } |
| r.RouteGroupName = routeGroup.Name |
| } |
| } |
| return nil |
| } |
| |
| func (arr *ApisixRouteRequest) Parse(r *RouteRequest) { |
| arr.Desc = r.Desc |
| arr.Priority = r.Priority |
| arr.Methods = r.Methods |
| arr.Uris = r.Uris |
| arr.Hosts = r.Hosts |
| arr.Vars = r.Vars |
| arr.Upstream = r.Upstream |
| arr.Plugins = r.Plugins |
| } |
| |
| func (rd *Route) Parse(r *RouteRequest, arr *ApisixRouteRequest) error { |
| //rd.Name = arr.Name |
| rd.Description = arr.Desc |
| rd.UpstreamId = r.UpstreamId |
| rd.RouteGroupId = r.RouteGroupId |
| rd.RouteGroupName = r.RouteGroupName |
| rd.Status = r.Status |
| if content, err := json.Marshal(r); err != nil { |
| return err |
| } else { |
| rd.Content = string(content) |
| } |
| if script, err := json.Marshal(r.Script); err != nil { |
| return err |
| } else { |
| rd.Script = string(script) |
| } |
| timestamp := time.Now().Unix() |
| rd.CreateTime = timestamp |
| rd.Priority = r.Priority |
| return nil |
| } |
| |
| func (arr *ApisixRouteRequest) FindById(rid string) (*ApisixRouteResponse, error) { |
| url := fmt.Sprintf("%s/routes/%s", conf.BaseUrl, rid) |
| if resp, err := utils.Get(url); err != nil { |
| logger.Error(err.Error()) |
| return nil, err |
| } else { |
| var arresp ApisixRouteResponse |
| if err := json.Unmarshal(resp, &arresp); err != nil { |
| logger.Error(err.Error()) |
| return nil, err |
| } else { |
| return &arresp, nil |
| } |
| } |
| } |
| |
| func (arr *ApisixRouteRequest) Update(rid string) (*ApisixRouteResponse, error) { |
| url := fmt.Sprintf("%s/routes/%s", conf.BaseUrl, rid) |
| if b, err := json.Marshal(arr); 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 ApisixRouteResponse |
| if err := json.Unmarshal(resp, &arresp); err != nil { |
| logger.Error(err.Error()) |
| return nil, err |
| } else { |
| return &arresp, nil |
| } |
| } |
| } |
| } |
| |
| func (arr *ApisixRouteRequest) Create(rid string) (*ApisixRouteResponse, error) { |
| url := fmt.Sprintf("%s/routes/%s", conf.BaseUrl, rid) |
| if b, err := json.Marshal(arr); 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 ApisixRouteResponse |
| if err := json.Unmarshal(resp, &arresp); err != nil { |
| logger.Error(err.Error()) |
| return nil, err |
| } else { |
| return &arresp, nil |
| } |
| } |
| } |
| } |
| |
| func (arr *ApisixRouteRequest) Delete(rid string) (*ApisixRouteResponse, error) { |
| url := fmt.Sprintf("%s/routes/%s", conf.BaseUrl, rid) |
| if resp, err := utils.Delete(url); err != nil { |
| logger.Error(err.Error()) |
| return nil, err |
| } else { |
| var arresp ApisixRouteResponse |
| if err := json.Unmarshal(resp, &arresp); err != nil { |
| logger.Error(err.Error()) |
| return nil, err |
| } else { |
| return &arresp, nil |
| } |
| } |
| } |
| |
| type RouteRequest struct { |
| ID string `json:"id,omitempty"` |
| Name string `json:"name"` |
| Desc string `json:"desc,omitempty"` |
| Priority int64 `json:"priority,omitempty"` |
| Methods []string `json:"methods,omitempty"` |
| Uris []string `json:"uris"` |
| Hosts []string `json:"hosts,omitempty"` |
| Protocols []string `json:"protocols,omitempty"` |
| 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"` |
| Plugins map[string]interface{} `json:"plugins"` |
| Script map[string]interface{} `json:"script"` |
| RouteGroupId string `json:"route_group_id"` |
| RouteGroupName string `json:"route_group_name"` |
| Status bool `json:"status"` |
| } |
| |
| func (r *ApisixRouteResponse) Parse() (*RouteRequest, error) { |
| o := r.Node.Value |
| |
| //Protocols from vars and upstream |
| protocols := make([]string, 0) |
| if o.Upstream != nil && o.Upstream.EnableWebsocket { |
| protocols = append(protocols, WEBSOCKET) |
| } |
| if o.UpstreamId != "" { |
| protocols = append(protocols, WEBSOCKET) |
| } |
| flag := true |
| for _, t := range o.Vars { |
| if t[0] == SCHEME { |
| flag = false |
| protocols = append(protocols, t[2]) |
| } |
| } |
| if flag { |
| protocols = append(protocols, HTTP) |
| protocols = append(protocols, HTTPS) |
| } |
| //Redirect from plugins |
| redirect := &Redirect{} |
| upstreamProtocol := UPATHTYPE_KEEP |
| upstreamHeader := make(map[string]string) |
| upstreamPath := &UpstreamPath{} |
| for k, v := range o.Plugins { |
| if k == REDIRECT { |
| if bytes, err := json.Marshal(v); err != nil { |
| return nil, err |
| } else { |
| if err := json.Unmarshal(bytes, redirect); err != nil { |
| return nil, err |
| } |
| } |
| |
| } |
| if k == PROXY_REWRIETE { |
| pr := &ProxyRewrite{} |
| if bytes, err := json.Marshal(v); err != nil { |
| return nil, err |
| } else { |
| if err := json.Unmarshal(bytes, pr); err != nil { |
| return nil, err |
| } else { |
| if pr.Scheme != "" { |
| upstreamProtocol = pr.Scheme |
| } |
| upstreamHeader = pr.Headers |
| if (pr.RegexUri == nil || len(pr.RegexUri) < 2) && pr.Uri == "" { |
| upstreamPath = nil |
| } else if pr.RegexUri == nil || len(pr.RegexUri) < 2 { |
| upstreamPath.UPathType = UPATHTYPE_STATIC |
| upstreamPath.To = pr.Uri |
| } else { |
| upstreamPath.UPathType = UPATHTYPE_REGX |
| upstreamPath.From = pr.RegexUri[0] |
| upstreamPath.To = pr.RegexUri[1] |
| } |
| } |
| } |
| } |
| } |
| //Vars |
| requestVars := make([][]string, 0) |
| for _, t := range o.Vars { |
| if t[0] != SCHEME { |
| requestVars = append(requestVars, t) |
| } |
| } |
| //Plugins |
| requestPlugins := utils.CopyMap(o.Plugins) |
| delete(requestPlugins, REDIRECT) |
| delete(requestPlugins, PROXY_REWRIETE) |
| |
| // check if upstream is not exist |
| if o.Upstream == nil && o.UpstreamId == "" { |
| upstreamProtocol = "" |
| upstreamHeader = nil |
| upstreamPath = nil |
| } |
| if upstreamPath != nil && upstreamPath.UPathType == "" { |
| upstreamPath = nil |
| } |
| result := &RouteRequest{ |
| ID: o.Id, |
| Desc: o.Desc, |
| Priority: o.Priority, |
| Methods: o.Methods, |
| Uris: o.Uris, |
| Hosts: o.Hosts, |
| Redirect: redirect, |
| Upstream: o.Upstream, |
| UpstreamId: o.UpstreamId, |
| RouteGroupId: o.RouteGroupId, |
| UpstreamProtocol: upstreamProtocol, |
| UpstreamPath: upstreamPath, |
| UpstreamHeader: upstreamHeader, |
| Protocols: protocols, |
| Vars: requestVars, |
| Plugins: requestPlugins, |
| } |
| return result, nil |
| } |
| |
| type Redirect struct { |
| HttpToHttps bool `json:"http_to_https,omitempty"` |
| Code int64 `json:"code,omitempty"` |
| Uri string `json:"uri,omitempty"` |
| } |
| |
| type ProxyRewrite struct { |
| Uri string `json:"uri"` |
| RegexUri []string `json:"regex_uri"` |
| Scheme string `json:"scheme"` |
| Host string `json:"host"` |
| Headers map[string]string `json:"headers"` |
| } |
| |
| func (r ProxyRewrite) MarshalJSON() ([]byte, error) { |
| m := make(map[string]interface{}) |
| if r.RegexUri != nil { |
| m["regex_uri"] = r.RegexUri |
| } |
| if r.Uri != "" { |
| m["uri"] = r.Uri |
| } |
| if r.Scheme != UPATHTYPE_KEEP && r.Scheme != "" { |
| m["scheme"] = r.Scheme |
| } |
| if r.Host != "" { |
| m["host"] = r.Host |
| } |
| if r.Headers != nil && len(r.Headers) > 0 { |
| m["headers"] = r.Headers |
| } |
| if result, err := json.Marshal(m); err != nil { |
| return nil, err |
| } else { |
| return result, nil |
| } |
| } |
| |
| func (r Redirect) MarshalJSON() ([]byte, error) { |
| m := make(map[string]interface{}) |
| if r.HttpToHttps { |
| m["http_to_https"] = true |
| } else if r.Uri != "" { |
| m["code"] = r.Code |
| m["uri"] = r.Uri |
| } |
| if result, err := json.Marshal(m); err != nil { |
| return nil, err |
| } else { |
| return result, nil |
| } |
| } |
| |
| type Upstream struct { |
| UType string `json:"type"` |
| Nodes map[string]int64 `json:"nodes"` |
| Timeout UpstreamTimeout `json:"timeout"` |
| EnableWebsocket bool `json:"enable_websocket"` |
| Checks map[string]interface{} `json:"checks,omitempty"` |
| HashOn string `json:"hash_on,omitempty"` |
| Key string `json:"key,omitempty"` |
| } |
| |
| type UpstreamTimeout struct { |
| Connect int64 `json:"connect"` |
| Send int64 `json:"send"` |
| Read int64 `json:"read"` |
| } |
| |
| type UpstreamPath struct { |
| UPathType string `json:"type"` |
| From string `json:"from"` |
| To string `json:"to"` |
| } |
| |
| type ApisixRouteRequest struct { |
| Desc string `json:"desc,omitempty"` |
| Priority int64 `json:"priority"` |
| Methods []string `json:"methods,omitempty"` |
| Uris []string `json:"uris,omitempty"` |
| 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"` |
| Script string `json:"script,omitempty"` |
| //Name string `json:"name"` |
| } |
| |
| // ApisixRouteResponse is response from apisix admin api |
| type ApisixRouteResponse struct { |
| Action string `json:"action"` |
| Node *Node `json:"node"` |
| } |
| |
| type Node struct { |
| Value Value `json:"value"` |
| ModifiedIndex uint64 `json:"modifiedIndex"` |
| } |
| |
| type Value struct { |
| Id string `json:"id"` |
| Name string `json:"name"` |
| Desc string `json:"desc,omitempty"` |
| Priority int64 `json:"priority"` |
| Methods []string `json:"methods"` |
| Uris []string `json:"uris"` |
| Hosts []string `json:"hosts"` |
| Vars [][]string `json:"vars"` |
| Upstream *Upstream `json:"upstream,omitempty"` |
| UpstreamId string `json:"upstream_id,omitempty"` |
| Plugins map[string]interface{} `json:"plugins"` |
| RouteGroupId string `json:"route_group_id"` |
| RouteGroupName string `json:"route_group_name"` |
| Status bool `json:"status"` |
| } |
| |
| type Route struct { |
| Base |
| Name string `json:"name"` |
| Description string `json:"description,omitempty"` |
| Hosts string `json:"hosts"` |
| Uris string `json:"uris"` |
| UpstreamNodes string `json:"upstream_nodes"` |
| UpstreamId string `json:"upstream_id"` |
| Priority int64 `json:"priority"` |
| Content string `json:"content"` |
| Script string `json:"script"` |
| ContentAdminApi string `json:"content_admin_api"` |
| RouteGroupId string `json:"route_group_id"` |
| RouteGroupName string `json:"route_group_name"` |
| Status bool `json:"status"` |
| } |
| |
| type RouteResponse struct { |
| Base |
| Name string `json:"name"` |
| Description string `json:"description,omitempty"` |
| Hosts []string `json:"hosts,omitempty"` |
| Uris []string `json:"uris,omitempty"` |
| Upstream *Upstream `json:"upstream,omitempty"` |
| UpstreamId string `json:"upstream_id,omitempty"` |
| Priority int64 `json:"priority"` |
| RouteGroupId string `json:"route_group_id"` |
| RouteGroupName string `json:"route_group_name"` |
| Status bool `json:"status"` |
| } |
| |
| type RouteResponseWithUrl struct { |
| RouteRequest |
| Url string `json:"url"` |
| } |
| |
| type ListResponse struct { |
| Count int `json:"count"` |
| Data interface{} `json:"data"` |
| } |
| |
| func (rr *RouteResponse) Parse(r *Route) { |
| rr.Base = r.Base |
| rr.Name = r.Name |
| rr.Description = r.Description |
| rr.UpstreamId = r.UpstreamId |
| rr.Priority = r.Priority |
| rr.RouteGroupId = r.RouteGroupId |
| rr.RouteGroupName = r.RouteGroupName |
| rr.Status = r.Status |
| // hosts |
| if len(r.Hosts) > 0 { |
| var hosts []string |
| if err := json.Unmarshal([]byte(r.Hosts), &hosts); err == nil { |
| rr.Hosts = hosts |
| } else { |
| logger.Error(err.Error()) |
| } |
| } |
| |
| // uris |
| if len(r.Uris) > 0 { |
| var uris []string |
| if err := json.Unmarshal([]byte(r.Uris), &uris); err == nil { |
| rr.Uris = uris |
| } |
| } |
| |
| // uris |
| var resp ApisixRouteResponse |
| if err := json.Unmarshal([]byte(r.ContentAdminApi), &resp); err == nil { |
| rr.Upstream = resp.Node.Value.Upstream |
| } |
| } |
| |
| // RouteRequest -> ApisixRouteRequest |
| func ToApisixRequest(routeRequest *RouteRequest) *ApisixRouteRequest { |
| // redirect -> plugins |
| plugins := utils.CopyMap(routeRequest.Plugins) |
| redirect := routeRequest.Redirect |
| if redirect != nil { |
| plugins["redirect"] = redirect |
| } |
| |
| logger.Info(routeRequest.Plugins) |
| |
| // scheme https and not http -> vars ['scheme', '==', 'https'] |
| pMap := utils.Set2Map(routeRequest.Protocols) |
| |
| arr := &ApisixRouteRequest{} |
| arr.Parse(routeRequest) |
| |
| // protocols[websokect] -> upstream |
| if pMap[WEBSOCKET] == 1 && arr.Upstream != nil { |
| arr.Upstream.EnableWebsocket = true |
| } |
| vars := utils.CopyStrings(routeRequest.Vars) |
| if pMap[HTTP] != 1 || pMap[HTTPS] != 1 { |
| if pMap[HTTP] == 1 { |
| vars = append(vars, []string{SCHEME, "==", HTTP}) |
| } |
| if pMap[HTTPS] == 1 { |
| vars = append(vars, []string{SCHEME, "==", HTTPS}) |
| } |
| } |
| if len(vars) > 0 { |
| arr.Vars = vars |
| } else { |
| arr.Vars = nil |
| } |
| // upstreamId |
| arr.UpstreamId = routeRequest.UpstreamId |
| // upstream protocol |
| if arr.Upstream != nil || arr.UpstreamId != "" { |
| pr := &ProxyRewrite{} |
| pr.Scheme = routeRequest.UpstreamProtocol |
| // upstream path |
| proxyPath := routeRequest.UpstreamPath |
| if proxyPath != nil { |
| if proxyPath.UPathType == UPATHTYPE_STATIC || proxyPath.UPathType == "" { |
| pr.Uri = proxyPath.To |
| pr.RegexUri = nil |
| } else { |
| pr.RegexUri = []string{proxyPath.From, proxyPath.To} |
| } |
| } |
| // upstream headers |
| pr.Headers = routeRequest.UpstreamHeader |
| if proxyPath != nil || pr.Scheme != UPATHTYPE_KEEP || (pr.Headers != nil && len(pr.Headers) > 0) { |
| plugins[PROXY_REWRIETE] = pr |
| } |
| } |
| |
| if plugins != nil && len(plugins) > 0 { |
| arr.Plugins = plugins |
| } else { |
| arr.Plugins = nil |
| } |
| |
| if routeRequest.Script != nil { |
| arr.Script, _ = generateLuaCode(routeRequest.Script) |
| } |
| |
| return arr |
| } |
| |
| func generateLuaCode(script map[string]interface{}) (string, error) { |
| scriptString, err := json.Marshal(script) |
| if err != nil { |
| return "", err |
| } |
| |
| cmd := exec.Command("sh", "-c", |
| "cd /go/manager-api/dag-to-lua/ && lua cli.lua "+ |
| "'"+string(scriptString)+"'") |
| |
| logger.Info("generate conf:", string(scriptString)) |
| |
| stdout, _ := cmd.StdoutPipe() |
| defer stdout.Close() |
| if err := cmd.Start(); err != nil { |
| logger.Info("generate err:", err) |
| return "", err |
| } |
| |
| result, _ := ioutil.ReadAll(stdout) |
| resData := string(result) |
| |
| logger.Info("generated code:", resData) |
| |
| return resData, nil |
| } |
| |
| func ToRoute(routeRequest *RouteRequest, |
| arr *ApisixRouteRequest, |
| u4 uuid.UUID, |
| resp *ApisixRouteResponse) (*Route, *errno.ManagerError) { |
| rd := &Route{} |
| if err := rd.Parse(routeRequest, arr); err != nil { |
| e := errno.FromMessage(errno.DBRouteCreateError, err.Error()) |
| return nil, e |
| } |
| if rd.Name == "" { |
| rd.Name = routeRequest.Name |
| } |
| rd.ID = u4 |
| // content_admin_api |
| if resp != nil { |
| resp.Node.Value.RouteGroupId = rd.RouteGroupId |
| resp.Node.Value.Status = rd.Status |
| if respStr, err := json.Marshal(resp); err != nil { |
| e := errno.FromMessage(errno.DBRouteCreateError, err.Error()) |
| return nil, e |
| } else { |
| rd.ContentAdminApi = string(respStr) |
| } |
| } |
| // hosts |
| hosts := routeRequest.Hosts |
| if hb, err := json.Marshal(hosts); err != nil { |
| e := errno.FromMessage(errno.DBRouteCreateError, err.Error()) |
| logger.Warn(e.Msg) |
| } else { |
| rd.Hosts = string(hb) |
| } |
| // uris |
| uris := routeRequest.Uris |
| if ub, err := json.Marshal(uris); err != nil { |
| e := errno.FromMessage(errno.DBRouteCreateError, err.Error()) |
| logger.Warn(e.Msg) |
| } else { |
| rd.Uris = string(ub) |
| } |
| // upstreamNodes |
| if routeRequest.Upstream != nil { |
| nodes := routeRequest.Upstream.Nodes |
| ips := make([]string, 0) |
| for k, _ := range nodes { |
| ips = append(ips, k) |
| } |
| if nb, err := json.Marshal(ips); err != nil { |
| e := errno.FromMessage(errno.DBRouteCreateError, err.Error()) |
| logger.Warn(e.Msg) |
| } else { |
| rd.UpstreamNodes = string(nb) |
| } |
| } |
| // upstreamId |
| rd.UpstreamId = arr.UpstreamId |
| return rd, nil |
| } |