@@ -4,13 +4,15 @@ import (
4
4
"context"
5
5
"encoding/csv"
6
6
"errors"
7
+ "fmt"
7
8
"io"
9
+ "reflect"
8
10
"sort"
11
+ "strconv"
9
12
"strings"
10
13
11
14
"github.com/go-redis/redis/v8"
12
15
"github.com/letsencrypt/attache/src/redis/config"
13
- "gopkg.in/yaml.v3"
14
16
)
15
17
16
18
// Client is a wrapper around an inner go-redis client.
@@ -22,7 +24,7 @@ type Client struct {
22
24
}
23
25
24
26
func (h * Client ) StateNewCheck () (bool , error ) {
25
- var infoMatchingNewNodes = redisClusterInfo {"fail" , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 }
27
+ var infoMatchingNewNodes = clusterInfo {"fail" , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 }
26
28
clusterInfo , err := h .GetClusterInfo ()
27
29
if err != nil {
28
30
return false , err
@@ -79,35 +81,77 @@ func (h *Client) GetReplicaNodes() ([]redisClusterNode, error) {
79
81
return nodes , nil
80
82
}
81
83
82
- type redisClusterInfo struct {
83
- State string `yaml :"cluster_state"`
84
- SlotsAssigned int `yaml :"cluster_slots_assigned"`
85
- SlotsOk int `yaml :"cluster_slots_ok"`
86
- SlotsPfail int `yaml :"cluster_slots_pfail"`
87
- SlotsFail int `yaml :"cluster_slots_fail"`
88
- KnownNodes int `yaml :"cluster_known_nodes"`
89
- Size int `yaml :"cluster_size"`
90
- CurrentEpoch int `yaml :"cluster_current_epoch"`
91
- MyEpoch int `yaml :"cluster_my_epoch"`
92
- StatsMessagesSent int `yaml :"cluster_stats_messages_sent"`
93
- StatsMessagesReceived int `yaml :"cluster_stats_messages_received"`
84
+ type clusterInfo struct {
85
+ State string `name :"cluster_state"`
86
+ SlotsAssigned int64 `name :"cluster_slots_assigned"`
87
+ SlotsOk int64 `name :"cluster_slots_ok"`
88
+ SlotsPfail int64 `name :"cluster_slots_pfail"`
89
+ SlotsFail int64 `name :"cluster_slots_fail"`
90
+ KnownNodes int64 `name :"cluster_known_nodes"`
91
+ Size int64 `name :"cluster_size"`
92
+ CurrentEpoch int64 `name :"cluster_current_epoch"`
93
+ MyEpoch int64 `name :"cluster_my_epoch"`
94
+ StatsMessagesSent int64 `name :"cluster_stats_messages_sent"`
95
+ StatsMessagesReceived int64 `name :"cluster_stats_messages_received"`
94
96
}
95
97
96
- func parseClusterInfoResult (result string ) (* redisClusterInfo , error ) {
97
- var clusterInfo redisClusterInfo
98
- err := yaml .Unmarshal ([]byte (strings .ReplaceAll (result , ":" , ": " )), & clusterInfo )
99
- if err != nil {
100
- return nil , err
98
+ func setClusterInfoField (name string , value string , ci * clusterInfo ) error {
99
+ outType := reflect .TypeOf (* ci )
100
+ outValue := reflect .ValueOf (ci ).Elem ()
101
+ for i := 0 ; i < outType .NumField (); i ++ {
102
+ field := outType .Field (i )
103
+ fieldValue := outValue .Field (i )
104
+
105
+ if ! fieldValue .IsValid () || ! fieldValue .CanSet () {
106
+ continue
107
+ }
108
+ fieldName := field .Tag .Get ("name" )
109
+ if fieldName != name {
110
+ continue
111
+ }
112
+
113
+ switch field .Type .Kind () {
114
+ case reflect .Int64 :
115
+ vInt , err := strconv .Atoi (value )
116
+ if err != nil {
117
+ return fmt .Errorf ("couldn't parse %q, value of %q, as int: %w" , value , name , err )
118
+ }
119
+ fieldValue .SetInt (int64 (vInt ))
120
+ return nil
121
+
122
+ case reflect .String :
123
+ fieldValue .SetString (value )
124
+ return nil
125
+ }
126
+ }
127
+ return nil
128
+ }
129
+
130
+ // unmarshalClusterInfo constructs a *clusterInfo by parsing the (INFO style) output
131
+ // of the 'cluster info' command as specified in:
132
+ // https://redis.io/commands/cluster-info.
133
+ func unmarshalClusterInfo (info string ) (* clusterInfo , error ) {
134
+ var c clusterInfo
135
+ for _ , line := range strings .Split (info , "\r \n " ) {
136
+ // https://redis.io/commands/info#return-value
137
+ if strings .HasPrefix (line , "#" ) || line == "" {
138
+ continue
139
+ }
140
+ kv := strings .SplitN (line , ":" , 2 )
141
+ err := setClusterInfoField (kv [0 ], kv [1 ], & c )
142
+ if err != nil {
143
+ return nil , fmt .Errorf ("failed to parse 'cluster info': %w" , err )
144
+ }
101
145
}
102
- return & clusterInfo , nil
146
+ return & c , nil
103
147
}
104
148
105
- func (h * Client ) GetClusterInfo () (* redisClusterInfo , error ) {
149
+ func (h * Client ) GetClusterInfo () (* clusterInfo , error ) {
106
150
info , err := h .Client .ClusterInfo (context .Background ()).Result ()
107
151
if err != nil {
108
152
return nil , err
109
153
}
110
- return parseClusterInfoResult (info )
154
+ return unmarshalClusterInfo (info )
111
155
}
112
156
113
157
type redisClusterNode struct {
0 commit comments