github.com/XiaoMi/Gaea@v1.2.5/cc/server.go (about)

     1  // Copyright 2019 The Gaea Authors. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cc
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"net/http"
    21  	"strings"
    22  
    23  	"github.com/gin-contrib/gzip"
    24  	"github.com/gin-gonic/gin"
    25  
    26  	"github.com/XiaoMi/Gaea/cc/service"
    27  	"github.com/XiaoMi/Gaea/log"
    28  	"github.com/XiaoMi/Gaea/models"
    29  )
    30  
    31  // Server admin server
    32  type Server struct {
    33  	cfg *models.CCConfig
    34  
    35  	engine   *gin.Engine
    36  	listener net.Listener
    37  
    38  	exitC chan struct{}
    39  }
    40  
    41  // RetHeader response header
    42  type RetHeader struct {
    43  	RetCode    int    `json:"ret_code"`
    44  	RetMessage string `json:"ret_message"`
    45  }
    46  
    47  // NewServer constructor of Server
    48  func NewServer(addr string, cfg *models.CCConfig) (*Server, error) {
    49  	srv := &Server{cfg: cfg, exitC: make(chan struct{})}
    50  	srv.engine = gin.New()
    51  
    52  	l, err := net.Listen("tcp", addr)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	srv.listener = l
    57  	srv.registerURL()
    58  	return srv, nil
    59  }
    60  
    61  func (s *Server) registerURL() {
    62  	api := s.engine.Group("/api/cc", gin.BasicAuth(gin.Accounts{s.cfg.AdminUserName: s.cfg.AdminPassword}))
    63  	api.Use(gin.Recovery())
    64  	api.Use(gzip.Gzip(gzip.DefaultCompression))
    65  	api.Use(func(c *gin.Context) {
    66  		c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
    67  	})
    68  	api.GET("/namespace/list", s.listNamespace)
    69  	api.GET("/namespace", s.queryNamespace)
    70  	api.GET("/namespace/detail/:name", s.detailNamespace)
    71  	api.PUT("/namespace/modify", s.modifyNamespace)
    72  	api.PUT("/namespace/delete/:name", s.delNamespace)
    73  	api.GET("/namespace/sqlfingerprint/:name", s.sqlFingerprint)
    74  	api.GET("/proxy/config/fingerprint", s.proxyConfigFingerprint)
    75  }
    76  
    77  // ListNamespaceResp list names of all namespace response
    78  type ListNamespaceResp struct {
    79  	RetHeader *RetHeader `json:"ret_header"`
    80  	Data      []string   `json:"data"`
    81  }
    82  
    83  // @Summary 返回所有namespace名称
    84  // @Description 获取集群名称, 返回所有namespace名称, 未传入为默认集群
    85  // @Produce  json
    86  // @Param cluster header string false "cluster name"
    87  // @Success 200 {object} ListNamespaceResp
    88  // @Security BasicAuth
    89  // @Router /api/cc/namespace/list [get]
    90  func (s *Server) listNamespace(c *gin.Context) {
    91  	var err error
    92  	r := &ListNamespaceResp{RetHeader: &RetHeader{RetCode: -1, RetMessage: ""}}
    93  	cluster := c.DefaultQuery("cluster", s.cfg.DefaultCluster)
    94  	r.Data, err = service.ListNamespace(s.cfg, cluster)
    95  	if err != nil {
    96  		log.Warn("list names of all namespace failed, %v", err)
    97  		r.RetHeader.RetMessage = err.Error()
    98  		c.JSON(http.StatusOK, r)
    99  		return
   100  	}
   101  	r.RetHeader.RetCode = 0
   102  	r.RetHeader.RetMessage = "SUCC"
   103  	c.JSON(http.StatusOK, r)
   104  	return
   105  }
   106  
   107  // QueryReq query namespace request
   108  type QueryReq struct {
   109  	Names []string `json:"names"`
   110  }
   111  
   112  // QueryNamespaceResp query namespace response
   113  type QueryNamespaceResp struct {
   114  	RetHeader *RetHeader          `json:"ret_header"`
   115  	Data      []*models.Namespace `json:"data"`
   116  }
   117  
   118  // @Summary 返回namespace配置详情, 已废弃
   119  // @Description 获取集群名称, 返回多个指定namespace配置详情, 未传入为默认集群, 已废弃
   120  // @Accept  json
   121  // @Produce  json
   122  // @Param cluster header string false "cluster name"
   123  // @Param names body json true "{"names":["namespace_1","namespace_2"]}"
   124  // @Success 200 {object} QueryNamespaceResp
   125  // @Security BasicAuth
   126  // @Router /api/cc/namespace [get]
   127  func (s *Server) queryNamespace(c *gin.Context) {
   128  	var err error
   129  	var req QueryReq
   130  	h := &RetHeader{RetCode: -1, RetMessage: ""}
   131  	r := &QueryNamespaceResp{RetHeader: h}
   132  
   133  	err = c.BindJSON(&req)
   134  	if err != nil {
   135  		log.Warn("queryNamespace got invalid data, err: %v", err)
   136  		h.RetMessage = err.Error()
   137  		c.JSON(http.StatusBadRequest, r)
   138  		return
   139  	}
   140  	cluster := c.DefaultQuery("cluster", s.cfg.DefaultCluster)
   141  	r.Data, err = service.QueryNamespace(req.Names, s.cfg, cluster)
   142  	if err != nil {
   143  		log.Warn("query namespace failed, %v", err)
   144  		c.JSON(http.StatusOK, r)
   145  		return
   146  	}
   147  
   148  	h.RetCode = 0
   149  	h.RetMessage = "SUCC"
   150  	c.JSON(http.StatusOK, r)
   151  	return
   152  }
   153  
   154  // @Summary 返回namespace配置详情
   155  // @Description 获取集群名称, 返回指定namespace配置详情, 未传入为默认集群
   156  // @Produce  json
   157  // @Param cluster header string false "cluster name"
   158  // @Param name path string true "namespace Name"
   159  // @Success 200 {object} QueryNamespaceResp
   160  // @Security BasicAuth
   161  // @Router /api/cc/namespace/detail/{name} [get]
   162  func (s *Server) detailNamespace(c *gin.Context) {
   163  	var err error
   164  	var names []string
   165  	h := &RetHeader{RetCode: -1, RetMessage: ""}
   166  	r := &QueryNamespaceResp{RetHeader: h}
   167  
   168  	name := strings.TrimSpace(c.Param("name"))
   169  	if name == "" {
   170  		h.RetMessage = "input name is empty"
   171  		c.JSON(http.StatusOK, h)
   172  		return
   173  	}
   174  
   175  	names = append(names, name)
   176  	cluster := c.DefaultQuery("cluster", s.cfg.DefaultCluster)
   177  	r.Data, err = service.QueryNamespace(names, s.cfg, cluster)
   178  	if err != nil {
   179  		log.Warn("query namespace failed, %v", err)
   180  		c.JSON(http.StatusOK, r)
   181  		return
   182  	}
   183  
   184  	h.RetCode = 0
   185  	h.RetMessage = "SUCC"
   186  	c.JSON(http.StatusOK, r)
   187  	return
   188  }
   189  
   190  // @Summary 创建修改namespace配置
   191  // @Description 获取集群名称, 根据json body创建或修改namespace配置, 未传入为默认集群
   192  // @Accept  json
   193  // @Produce  json
   194  // @Param cluster header string false "cluster name"
   195  // @Param name body json true "namespace"
   196  // @Success 200 {object} RetHeader
   197  // @Security BasicAuth
   198  // @Router /api/cc/namespace/modify [put]
   199  func (s *Server) modifyNamespace(c *gin.Context) {
   200  	var err error
   201  	var namespace models.Namespace
   202  	h := &RetHeader{RetCode: -1, RetMessage: ""}
   203  
   204  	err = c.BindJSON(&namespace)
   205  	if err != nil {
   206  		log.Warn("modifyNamespace failed, err: %v", err)
   207  		c.JSON(http.StatusBadRequest, h)
   208  		return
   209  	}
   210  	cluster := c.DefaultQuery("cluster", s.cfg.DefaultCluster)
   211  	err = service.ModifyNamespace(&namespace, s.cfg, cluster)
   212  	if err != nil {
   213  		log.Warn("modifyNamespace failed, err: %v", err)
   214  		h.RetMessage = err.Error()
   215  		c.JSON(http.StatusOK, h)
   216  		return
   217  	}
   218  
   219  	h.RetCode = 0
   220  	h.RetMessage = "SUCC"
   221  	c.JSON(http.StatusOK, h)
   222  	return
   223  }
   224  
   225  // @Summary 删除namespace配置
   226  // @Description 获取集群名称, 根据namespace name删除namespace, 未传入为默认集群
   227  // @Produce  json
   228  // @Param cluster header string false "cluster name"
   229  // @Param name path string true "namespace name"
   230  // @Success 200 {object} RetHeader
   231  // @Security BasicAuth
   232  // @Router /api/cc/namespace/delete/{name} [put]
   233  func (s *Server) delNamespace(c *gin.Context) {
   234  	var err error
   235  	h := &RetHeader{RetCode: -1, RetMessage: ""}
   236  	name := strings.TrimSpace(c.Param("name"))
   237  	if name == "" {
   238  		h.RetMessage = "input name is empty"
   239  		c.JSON(http.StatusOK, h)
   240  		return
   241  	}
   242  	cluster := c.DefaultQuery("cluster", s.cfg.DefaultCluster)
   243  	err = service.DelNamespace(name, s.cfg, cluster)
   244  	if err != nil {
   245  		h.RetMessage = fmt.Sprintf("delete namespace faild, %v", err.Error())
   246  		c.JSON(http.StatusOK, h)
   247  		return
   248  	}
   249  
   250  	h.RetCode = 0
   251  	h.RetMessage = "SUCC"
   252  	c.JSON(http.StatusOK, h)
   253  	return
   254  }
   255  
   256  type sqlFingerprintResp struct {
   257  	RetHeader *RetHeader        `json:"ret_header"`
   258  	ErrSQLs   map[string]string `json:"err_sqls"`
   259  	SlowSQLs  map[string]string `json:"slow_sqls"`
   260  }
   261  
   262  // @Summary 获取namespce慢SQL、错误SQL
   263  // @Description 获取集群名称, 根据namespace name获取该namespce慢SQL、错误SQL, 未传入为默认集群
   264  // @Produce  json
   265  // @Param cluster header string false "cluster name"
   266  // @Param name path string true "namespace name"
   267  // @Success 200 {object} sqlFingerprintResp
   268  // @Security BasicAuth
   269  // @Router /api/cc/namespace/sqlfingerprint/{name} [get]
   270  func (s *Server) sqlFingerprint(c *gin.Context) {
   271  	var err error
   272  	r := &sqlFingerprintResp{RetHeader: &RetHeader{RetCode: -1, RetMessage: ""}}
   273  	name := strings.TrimSpace(c.Param("name"))
   274  	if name == "" {
   275  		r.RetHeader.RetMessage = "input name is empty"
   276  		c.JSON(http.StatusOK, r)
   277  		return
   278  	}
   279  	cluster := c.DefaultQuery("cluster", s.cfg.DefaultCluster)
   280  	r.SlowSQLs, r.ErrSQLs, err = service.SQLFingerprint(name, s.cfg, cluster)
   281  	if err != nil {
   282  		r.RetHeader.RetMessage = err.Error()
   283  		c.JSON(http.StatusOK, r)
   284  		return
   285  	}
   286  	r.RetHeader.RetCode = 0
   287  	r.RetHeader.RetMessage = "SUCC"
   288  	c.JSON(http.StatusOK, r)
   289  	return
   290  }
   291  
   292  type proxyConfigFingerprintResp struct {
   293  	RetHeader *RetHeader        `json:"ret_header"`
   294  	Data      map[string]string `json:"data"` // key: ip:port value: md5 of config
   295  }
   296  
   297  // @Summary 获取集群管理地址
   298  // @Description 获取集群名称, 返回集群管理地址, 未传入为默认集群
   299  // @Produce  json
   300  // @Param cluster header string false "cluster name"
   301  // @Success 200 {object} proxyConfigFingerprintResp
   302  // @Security BasicAuth
   303  // @Router /api/cc/proxy/config/fingerprint [get]
   304  func (s *Server) proxyConfigFingerprint(c *gin.Context) {
   305  	var err error
   306  	r := &proxyConfigFingerprintResp{RetHeader: &RetHeader{RetCode: -1, RetMessage: ""}}
   307  	cluster := c.DefaultQuery("cluster", s.cfg.DefaultCluster)
   308  	r.Data, err = service.ProxyConfigFingerprint(s.cfg, cluster)
   309  	if err != nil {
   310  		r.RetHeader.RetMessage = err.Error()
   311  		c.JSON(http.StatusOK, r)
   312  		return
   313  	}
   314  	r.RetHeader.RetCode = 0
   315  	r.RetHeader.RetMessage = "SUCC"
   316  	c.JSON(http.StatusOK, r)
   317  	return
   318  }
   319  
   320  func (s *Server) Run() {
   321  	defer s.listener.Close()
   322  
   323  	errC := make(chan error)
   324  
   325  	go func(l net.Listener) {
   326  		h := http.NewServeMux()
   327  		h.Handle("/", s.engine)
   328  		hs := &http.Server{Handler: h}
   329  		errC <- hs.Serve(l)
   330  	}(s.listener)
   331  
   332  	select {
   333  	case <-s.exitC:
   334  		log.Notice("server exit.")
   335  		return
   336  	case err := <-errC:
   337  		log.Fatal("gaea cc serve failed, %v", err)
   338  		return
   339  	}
   340  
   341  }
   342  
   343  func (s *Server) Close() {
   344  	s.exitC <- struct{}{}
   345  	return
   346  }