github.com/therealbill/libredis@v0.0.0-20161227004305-7d50abda5ccf/client/sentinel.go (about) 1 package client 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "reflect" 8 "strconv" 9 "strings" 10 11 "github.com/therealbill/libredis/structures" 12 ) 13 14 // buildSlaveInfoStruct builods the struct for a slave from the Redis slaves command 15 func (r *Redis) buildSlaveInfoStruct(info map[string]string) (master structures.SlaveInfo, err error) { 16 s := reflect.ValueOf(&master).Elem() 17 typeOfT := s.Type() 18 for i := 0; i < s.NumField(); i++ { 19 p := typeOfT.Field(i) 20 f := s.Field(i) 21 tag := p.Tag.Get("redis") 22 if f.Type().Name() == "int" { 23 val, err := strconv.ParseInt(info[tag], 10, 64) 24 if err != nil { 25 //println("Unable to convert to data from sentinel server:", info[tag], err) 26 } else { 27 f.SetInt(val) 28 } 29 } 30 if f.Type().Name() == "string" { 31 f.SetString(info[tag]) 32 } 33 if f.Type().Name() == "bool" { 34 // This handles primarily the xxx_xx style fields in the return data from redis 35 if info[tag] != "" { 36 val, err := strconv.ParseInt(info[tag], 10, 64) 37 if err != nil { 38 //println("[bool] Unable to convert to data from sentinel server:", info[tag], err) 39 fmt.Println("Error:", err) 40 } else { 41 if val > 0 { 42 f.SetBool(true) 43 } 44 } 45 } 46 } 47 } 48 return 49 } 50 51 // SentinelSlaves takes a podname and returns a list of SlaveInfo structs for 52 // each known slave. 53 func (r *Redis) SentinelSlaves(podname string) (slaves []structures.SlaveInfo, err error) { 54 rp, err := r.ExecuteCommand("SENTINEL", "SLAVES", podname) 55 if err != nil { 56 fmt.Println("error on slaves command:", err) 57 return 58 } 59 for i := 0; i < len(rp.Multi); i++ { 60 slavemap, err := rp.Multi[i].HashValue() 61 if err != nil { 62 log.Println("unable to get slave info, err:", err) 63 } else { 64 info, err := r.buildSlaveInfoStruct(slavemap) 65 if err != nil { 66 fmt.Printf("Unable to get slaves, err: %s\n", err) 67 } 68 slaves = append(slaves, info) 69 } 70 } 71 return 72 } 73 74 // SentinelMonitor executes the SENTINEL MONITOR command on the server 75 // This is used to add pods to the sentinel configuration 76 func (r *Redis) SentinelMonitor(podname string, ip string, port int, quorum int) (bool, error) { 77 res, err := r.ExecuteCommand("SENTINEL", "MONITOR", podname, ip, port, quorum) 78 if err != nil { 79 return false, err 80 } 81 return res.BoolValue() 82 } 83 84 // SentinelRemove executes the SENTINEL REMOVE command on the server 85 // This is used to remove pods to the sentinel configuration 86 func (r *Redis) SentinelRemove(podname string) (bool, error) { 87 res, err := r.ExecuteCommand("SENTINEL", "REMOVE", podname) 88 rsp, _ := res.StatusValue() 89 if rsp == "OK" { 90 return true, err 91 } else { 92 return false, err 93 } 94 } 95 96 func (r *Redis) SentinelReset(podname string) error { 97 res, err := r.ExecuteCommand("SENTINEL", "RESET", podname) 98 log.Print(res) 99 log.Print(res) 100 return err 101 } 102 103 // SentinelSetString will set the value of skey to sval for a 104 // given pod. This is used when the value is known to be a string 105 func (r *Redis) SentinelSetString(podname string, skey string, sval string) error { 106 _, err := r.ExecuteCommand("SENTINEL", "SET", podname, skey, sval) 107 return err 108 } 109 110 // SentinelSetInt will set the value of skey to sval for a 111 // given pod. This is used when the value is known to be an Int 112 func (r *Redis) SentinelSetInt(podname string, skey string, sval int) error { 113 _, err := r.ExecuteCommand("SENTINEL", "SET", podname, skey, sval) 114 return err 115 } 116 117 // SentinelSetPass will set the value to be used in the AUTH command for a 118 // given pod 119 func (r *Redis) SentinelSetPass(podname string, password string) error { 120 _, err := r.ExecuteCommand("SENTINEL", "SET", podname, "AUTHPASS", password) 121 return err 122 } 123 124 // SentinelSentinels returns the list of known Sentinels 125 func (r *Redis) SentinelSentinels(podName string) (sentinels []structures.SentinelInfo, err error) { 126 reply, err := r.ExecuteCommand("SENTINEL", "SENTINELS", podName) 127 if err != nil { 128 log.Print("Err in sentinels command:", err) 129 return 130 } 131 count := len(reply.Multi) 132 for i := 0; i < count; i++ { 133 data, err := reply.Multi[i].HashValue() 134 if err != nil { 135 log.Fatal("Error:", err) 136 } 137 sentinel, err := r.buildSentinelInfoStruct(data) 138 sentinels = append(sentinels, sentinel) 139 } 140 return 141 } 142 143 // SentinelMasters returns the list of known pods 144 func (r *Redis) SentinelMasters() (masters []structures.MasterInfo, err error) { 145 rp, err := r.ExecuteCommand("SENTINEL", "MASTERS") 146 if err != nil { 147 return 148 } 149 podcount := len(rp.Multi) 150 for i := 0; i < podcount; i++ { 151 pod, err := rp.Multi[i].HashValue() 152 if err != nil { 153 log.Fatal("Error:", err) 154 } 155 minfo, err := r.buildMasterInfoStruct(pod) 156 masters = append(masters, minfo) 157 } 158 return 159 } 160 161 // SentinelMaster returns the master info for the given podname 162 func (r *Redis) SentinelMaster(podname string) (master structures.MasterInfo, err error) { 163 rp, err := r.ExecuteCommand("SENTINEL", "MASTER", podname) 164 if err != nil { 165 return 166 } 167 pod, err := rp.HashValue() 168 if err != nil { 169 return 170 } 171 master, err = r.buildMasterInfoStruct(pod) 172 return 173 } 174 175 func (r *Redis) buildSentinelInfoStruct(info map[string]string) (sentinel structures.SentinelInfo, err error) { 176 s := reflect.ValueOf(&sentinel).Elem() 177 typeOfT := s.Type() 178 for i := 0; i < s.NumField(); i++ { 179 p := typeOfT.Field(i) 180 f := s.Field(i) 181 tag := p.Tag.Get("redis") 182 if f.Type().Name() == "int" { 183 val, err := strconv.ParseInt(info[tag], 10, 64) 184 if err != nil { 185 //fmt.Println("Unable to convert to integer from sentinel server:", tag, info[tag], err) 186 } else { 187 f.SetInt(val) 188 } 189 } 190 if f.Type().Name() == "string" { 191 f.SetString(info[tag]) 192 } 193 if f.Type().Name() == "bool" { 194 // This handles primarily the xxx_xx style fields in the return data from redis 195 if info[tag] != "" { 196 val, err := strconv.ParseInt(info[tag], 10, 64) 197 if err != nil { 198 //println("Unable to convert to bool from sentinel server:", info[tag]) 199 fmt.Println(info[tag]) 200 fmt.Println("Error:", err) 201 } else { 202 if val > 0 { 203 f.SetBool(true) 204 } 205 } 206 } 207 } 208 } 209 return 210 } 211 212 func (r *Redis) buildMasterInfoStruct(info map[string]string) (master structures.MasterInfo, err error) { 213 s := reflect.ValueOf(&master).Elem() 214 typeOfT := s.Type() 215 for i := 0; i < s.NumField(); i++ { 216 p := typeOfT.Field(i) 217 f := s.Field(i) 218 tag := p.Tag.Get("redis") 219 if f.Type().Name() == "int" { 220 if info[tag] > "" { 221 val, err := strconv.ParseInt(info[tag], 10, 64) 222 if err != nil { 223 fmt.Println("Unable to convert to integer from sentinel server:", tag, info[tag], err) 224 } else { 225 f.SetInt(val) 226 } 227 } 228 } 229 if f.Type().Name() == "string" { 230 f.SetString(info[tag]) 231 } 232 if f.Type().Name() == "bool" { 233 // This handles primarily the xxx_xx style fields in the return data from redis 234 if info[tag] != "" { 235 val, err := strconv.ParseInt(info[tag], 10, 64) 236 if err != nil { 237 //println("Unable to convert to bool from sentinel server:", info[tag]) 238 fmt.Println(info[tag]) 239 fmt.Println("Error:", err) 240 } else { 241 if val > 0 { 242 f.SetBool(true) 243 } 244 } 245 } 246 } 247 } 248 return 249 } 250 251 // SentinelMasterInfo returns the information about a pod or master 252 func (r *Redis) SentinelMasterInfo(podname string) (master structures.MasterInfo, err error) { 253 rp, err := r.ExecuteCommand("SENTINEL", "MASTER", podname) 254 if err != nil { 255 return master, err 256 } 257 info, err := rp.HashValue() 258 return r.buildMasterInfoStruct(info) 259 } 260 261 // SentinelGetMaster returns the information needed to connect to the master of 262 // a given pod 263 func (r *Redis) SentinelGetMaster(podname string) (conninfo structures.MasterAddress, err error) { 264 rp, err := r.ExecuteCommand("SENTINEL", "get-master-addr-by-name", podname) 265 if err != nil { 266 return conninfo, err 267 } 268 info, err := rp.ListValue() 269 if len(info) != 0 { 270 conninfo.Host = info[0] 271 conninfo.Port, err = strconv.Atoi(info[1]) 272 if err != nil { 273 fmt.Println("Got bad port info from server, causing err:", err) 274 } 275 } 276 return conninfo, err 277 } 278 279 func (r *Redis) SentinelFailover(podname string) (bool, error) { 280 rp, err := r.ExecuteCommand("SENTINEL", "failover", podname) 281 if err != nil { 282 log.Println("Error on failover command execution:", err) 283 return false, err 284 } 285 286 if rp.Error != "" { 287 log.Println("Error on failover command execution:", rp.Error) 288 return false, fmt.Errorf(rp.Error) 289 } 290 return true, nil 291 } 292 293 func (r *Redis) GetPodName() (name string, err error) { 294 // we can get this by subscribing to the sentinel channel and getting a hello message 295 // TODO: Will need to update this when I make PubSub message structs 296 maxmsgcnt := 10 297 p, _ := r.PubSub() 298 p.Subscribe("__sentinel__:hello") 299 var msg []string 300 for x := 0; x < maxmsgcnt; x++ { 301 msg, err = p.Receive() 302 if err != nil { 303 return name, err 304 } 305 switch msg[0] { 306 case "message": 307 fields := strings.Split(msg[2], ",") 308 name = fields[4] 309 return name, nil 310 } 311 } 312 return name, errors.New("No messages contained the needed hello") 313 } 314 315 func (r *Redis) GetKnownSentinels() (sentinels map[string]string, err error) { 316 maxmsgcnt := 10 317 p, _ := r.PubSub() 318 p.Subscribe("__sentinel__:hello") 319 sentinels = make(map[string]string) 320 var msg []string 321 for x := 0; x < maxmsgcnt; x++ { 322 msg, err = p.Receive() 323 if err != nil { 324 return 325 } 326 switch msg[0] { 327 case "message": 328 fields := strings.Split(msg[2], ",") 329 //name = fields[4] 330 ip := fields[0] 331 port := fields[1] 332 //sentinelid := fields[2] 333 sentinels[ip] = port 334 } 335 } 336 if len(sentinels) == 0 { 337 return sentinels, errors.New("No messages contained the needed hello") 338 } else { 339 return sentinels, nil 340 } 341 }