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 }