github.com/XiaoMi/Gaea@v1.2.5/models/namespace.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 models 16 17 import ( 18 "encoding/base64" 19 "errors" 20 "fmt" 21 "strconv" 22 "strings" 23 24 "github.com/XiaoMi/Gaea/mysql" 25 "github.com/XiaoMi/Gaea/util" 26 "github.com/XiaoMi/Gaea/util/crypto" 27 ) 28 29 // Namespace means namespace model stored in etcd 30 type Namespace struct { 31 OpenGeneralLog bool `json:"open_general_log"` 32 IsEncrypt bool `json:"is_encrypt"` // true: 加密存储 false: 非加密存储,目前加密Slice、User中的用户名、密码 33 Name string `json:"name"` 34 Online bool `json:"online"` 35 ReadOnly bool `json:"read_only"` 36 AllowedDBS map[string]bool `json:"allowed_dbs"` 37 DefaultPhyDBS map[string]string `json:"default_phy_dbs"` 38 SlowSQLTime string `json:"slow_sql_time"` 39 BlackSQL []string `json:"black_sql"` 40 AllowedIP []string `json:"allowed_ip"` 41 Slices []*Slice `json:"slices"` 42 ShardRules []*Shard `json:"shard_rules"` 43 Users []*User `json:"users"` // 客户端接入proxy用户,每个用户可以设置读写分离、读写权限等 44 DefaultSlice string `json:"default_slice"` 45 GlobalSequences []*GlobalSequence `json:"global_sequences"` 46 DefaultCharset string `json:"default_charset"` 47 DefaultCollation string `json:"default_collation"` 48 MaxSqlExecuteTime int `json:"max_sql_execute_time"` // sql最大执行时间,大于该时间,进行熔断 49 MaxSqlResultSize int `json:"max_sql_result_size"` // 限制单分片返回结果集大小不超过max_select_rows 50 } 51 52 // Encode encode json 53 func (n *Namespace) Encode() []byte { 54 return JSONEncode(n) 55 } 56 57 // Verify verify namespace contents 58 func (n *Namespace) Verify() error { 59 if err := n.verifyName(); err != nil { 60 return err 61 } 62 63 if err := n.verifyAllowDBS(); err != nil { 64 return err 65 } 66 67 if err := n.verifyUsers(); err != nil { 68 return err 69 } 70 71 if err := n.verifySlowSQLTime(); err != nil { 72 return err 73 } 74 75 if err := n.verifyDBs(); err != nil { 76 return err 77 } 78 79 if err := n.verifyAllowIps(); err != nil { 80 return err 81 } 82 83 if err := n.verifyCharset(); err != nil { 84 return err 85 } 86 87 if err := n.verifySlices(); err != nil { 88 return err 89 } 90 91 if err := n.verifyDefaultSlice(); err != nil { 92 return err 93 } 94 95 if err := n.verifyShardRules(); err != nil { 96 return err 97 } 98 99 return nil 100 } 101 102 func (n *Namespace) verifyName() error { 103 if !n.isNameExists() { 104 return fmt.Errorf("must specify namespace name") 105 } 106 return nil 107 } 108 109 func (n *Namespace) isNameExists() bool { 110 return n.Name != "" 111 } 112 113 func (n *Namespace) verifyAllowDBS() error { 114 if n.isAllowedDBSEmpty() { 115 return errors.New("must specify usable dbs") 116 } 117 return nil 118 } 119 120 func (n *Namespace) isAllowedDBSEmpty() bool { 121 return len(n.AllowedDBS) == 0 122 } 123 124 func (n *Namespace) verifyUsers() error { 125 if n.isUsersEmpty() { 126 return errors.New("must specify proxy access users") 127 } 128 129 for i, u := range n.Users { 130 //check namespace 131 if u.Namespace == "" { 132 u.Namespace = n.Name 133 } else if u.Namespace != n.Name { 134 return fmt.Errorf("user's namespace name mismatch, user: %s, namespace: %s, %s", u.UserName, n.Name, u.Namespace) 135 } 136 137 if err := u.verify(); err != nil { 138 return fmt.Errorf("user config error, schema: %s, %v", n.Name, err) 139 } 140 141 //check repeat username 142 for j := 0; j < i; j++ { 143 if n.Users[j].UserName == u.UserName { 144 return fmt.Errorf("user duped, namespace: %s, user: %s", n.Name, u.UserName) 145 } 146 } 147 } 148 return nil 149 } 150 151 func (n *Namespace) isUsersEmpty() bool { 152 return len(n.Users) == 0 153 } 154 155 func (n *Namespace) verifySlowSQLTime() error { 156 if !n.isSlowSQLTimeExists() { 157 return nil 158 } 159 if err := n.isSlowSQLTimeValid(); err != nil { 160 return err 161 } 162 return nil 163 } 164 165 func (n *Namespace) isSlowSQLTimeExists() bool { 166 return n.SlowSQLTime != "" 167 } 168 169 func (n *Namespace) isSlowSQLTimeValid() error { 170 if slowSQLTime, err := strconv.ParseInt(n.SlowSQLTime, 10, 64); err != nil || slowSQLTime < 0 { 171 return errors.New("invalid slow sql time") 172 } 173 return nil 174 } 175 176 func (n *Namespace) verifyDBs() error { 177 // no logic database mode 178 if n.isDefaultPhyDBSEmpty() { 179 return nil 180 } 181 182 // logic database mode 183 if err := n.isAllowedDBSValid(); err != nil { 184 return err 185 } 186 return nil 187 } 188 189 func (n *Namespace) isDefaultPhyDBSEmpty() bool { 190 return len(n.DefaultPhyDBS) == 0 191 } 192 193 func (n *Namespace) isAllowedDBSValid() error { 194 for db := range n.AllowedDBS { 195 if _, ok := n.DefaultPhyDBS[db]; !ok { 196 return fmt.Errorf("db %s have no phy db", db) 197 } 198 } 199 return nil 200 } 201 202 func (n *Namespace) verifyAllowIps() error { 203 for _, ipStr := range n.AllowedIP { 204 ipStr = strings.TrimSpace(ipStr) 205 if len(ipStr) == 0 { 206 continue 207 } 208 209 if _, err := util.ParseIPInfo(ipStr); err != nil { 210 return fmt.Errorf("verify allowips error: %v", err) 211 } 212 } 213 return nil 214 } 215 216 func (n *Namespace) verifyCharset() error { 217 if err := mysql.VerifyCharset(n.DefaultCharset, n.DefaultCollation); err != nil { 218 return fmt.Errorf("verify charset error: %v", err) 219 } 220 return nil 221 } 222 223 func (n *Namespace) verifySlices() error { 224 if n.isSlicesEmpty() { 225 return errors.New("empty slices") 226 } 227 if err := n.verifyEachSlice(); err != nil { 228 return err 229 } 230 return nil 231 } 232 233 func (n *Namespace) isSlicesEmpty() bool { 234 return len(n.Slices) == 0 235 } 236 237 func (n *Namespace) verifyEachSlice() error { 238 for i, slice := range n.Slices { 239 if err := slice.verify(); err != nil { 240 return fmt.Errorf("slice cfg error, namespace: %s, err: %s", n.Name, err.Error()) 241 } 242 243 //check repeat slice 244 for j := 0; j < i; j++ { 245 if n.Slices[j].Name == slice.Name { 246 return fmt.Errorf("slice name duped, namespace: %s, slice: %s", n.Name, slice.Name) 247 } 248 } 249 } 250 return nil 251 } 252 253 func (n *Namespace) verifyDefaultSlice() error { 254 if n.DefaultSlice != "" { 255 exist := false 256 for _, slice := range n.Slices { 257 if slice.Name == n.DefaultSlice { 258 exist = true 259 break 260 } 261 } 262 263 if !exist { 264 return fmt.Errorf("invalid default slice: %s", n.DefaultSlice) 265 } 266 } 267 return nil 268 } 269 270 func (n *Namespace) verifyShardRules() error { 271 var sliceNames []string 272 var linkedRuleShards []*Shard 273 var rules = make(map[string]map[string]string) 274 275 for _, slice := range n.Slices { 276 sliceNames = append(sliceNames, slice.Name) 277 } 278 279 for _, s := range n.ShardRules { 280 for _, slice := range s.Slices { 281 if !includeSlice(sliceNames, slice) { 282 return fmt.Errorf("shard table[%s] slice[%s] not in the namespace.slices list:[%s]", 283 s.Table, slice, strings.Join(s.Slices, ",")) 284 } 285 } 286 287 switch s.Type { 288 case ShardDefault: 289 return errors.New("[default-rule] duplicate, must only one") 290 // get index of linked table config and handle it later 291 case ShardLinked: 292 linkedRuleShards = append(linkedRuleShards, s) 293 default: 294 if err := s.verify(); err != nil { 295 return err 296 } 297 } 298 299 //if the database exist in rules 300 if _, ok := rules[s.DB]; ok { 301 if _, ok := rules[s.DB][s.Table]; ok { 302 return fmt.Errorf("table %s rule in %s duplicate", s.Table, s.DB) 303 } else { 304 rules[s.DB][s.Table] = s.Type 305 } 306 } else { 307 m := make(map[string]string) 308 rules[s.DB] = m 309 rules[s.DB][s.Table] = s.Type 310 } 311 } 312 313 for _, s := range linkedRuleShards { 314 tableRules, ok := rules[s.DB] 315 if !ok { 316 return fmt.Errorf("db of LinkedRule is not found in parent rules") 317 } 318 dbRuleType, ok := tableRules[s.ParentTable] 319 if !ok { 320 return fmt.Errorf("parent table of LinkedRule is not found in parent rules") 321 } 322 if dbRuleType == ShardLinked { 323 return fmt.Errorf("LinkedRule cannot link to another LinkedRule") 324 } 325 } 326 return nil 327 } 328 329 // Decrypt decrypt user/password in namespace 330 func (n *Namespace) Decrypt(key string) (err error) { 331 if !n.IsEncrypt { 332 return nil 333 } 334 // Users 335 for i := range n.Users { 336 n.Users[i].UserName, err = decrypt(key, n.Users[i].UserName) 337 if err != nil { 338 return 339 } 340 n.Users[i].Password, err = decrypt(key, n.Users[i].Password) 341 if err != nil { 342 return 343 } 344 } 345 // Slices 346 for i := range n.Slices { 347 n.Slices[i].UserName, err = decrypt(key, n.Slices[i].UserName) 348 if err != nil { 349 return 350 } 351 n.Slices[i].Password, err = decrypt(key, n.Slices[i].Password) 352 if err != nil { 353 return 354 } 355 } 356 357 return nil 358 } 359 360 // Encrypt encrypt user/password in namespace 361 func (n *Namespace) Encrypt(key string) (err error) { 362 n.IsEncrypt = true 363 // Users 364 for i := range n.Users { 365 n.Users[i].UserName, err = encrypt(key, n.Users[i].UserName) 366 if err != nil { 367 return 368 } 369 n.Users[i].Password, err = encrypt(key, n.Users[i].Password) 370 if err != nil { 371 return 372 } 373 } 374 // Slices 375 for i := range n.Slices { 376 n.Slices[i].UserName, err = encrypt(key, n.Slices[i].UserName) 377 if err != nil { 378 return 379 } 380 n.Slices[i].Password, err = encrypt(key, n.Slices[i].Password) 381 if err != nil { 382 return 383 } 384 } 385 386 return nil 387 } 388 389 func decrypt(key, data string) (string, error) { 390 t, _ := base64.StdEncoding.DecodeString(data) 391 origin, err := crypto.DecryptECB(key, t) 392 if err != nil { 393 return "", err 394 } 395 return string(origin), nil 396 } 397 398 func encrypt(key, data string) (string, error) { 399 tmp, err := crypto.EncryptECB(key, []byte(data)) 400 if err != nil { 401 return "", err 402 } 403 base64Str := base64.StdEncoding.EncodeToString(tmp) 404 return base64Str, nil 405 }