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  }