vitess.io/vitess@v0.16.2/go/vt/vtadmin/cluster/discovery/discovery_json.go (about) 1 /* 2 Copyright 2022 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package discovery 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "math/rand" 24 25 "vitess.io/vitess/go/trace" 26 27 vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" 28 ) 29 30 // JSONDiscovery implements the Discovery interface for "discovering" 31 // Vitess components hardcoded in a json object. 32 // 33 // StaticFileDiscovery and DynamicDiscovery inherit from JSONDiscovery because 34 // they both read the same JSON object. They only differ in where the JSON object is stored. 35 // 36 // As an example, here's a minimal JSON file for a single Vitess cluster running locally 37 // (such as the one described in https://vitess.io/docs/get-started/local-docker): 38 // 39 // { 40 // "vtgates": [ 41 // { 42 // "host": { 43 // "hostname": "127.0.0.1:15991" 44 // } 45 // } 46 // ] 47 // } 48 // 49 // For more examples of various static file configurations, see the unit tests. 50 type JSONDiscovery struct { 51 cluster *vtadminpb.Cluster 52 config *JSONClusterConfig 53 gates struct { 54 byName map[string]*vtadminpb.VTGate 55 byTag map[string][]*vtadminpb.VTGate 56 } 57 vtctlds struct { 58 byName map[string]*vtadminpb.Vtctld 59 byTag map[string][]*vtadminpb.Vtctld 60 } 61 } 62 63 // JSONClusterConfig configures Vitess components for a single cluster. 64 type JSONClusterConfig struct { 65 VTGates []*JSONVTGateConfig `json:"vtgates,omitempty"` 66 Vtctlds []*JSONVtctldConfig `json:"vtctlds,omitempty"` 67 } 68 69 // JSONVTGateConfig contains host and tag information for a single VTGate in a cluster. 70 type JSONVTGateConfig struct { 71 Host *vtadminpb.VTGate `json:"host"` 72 Tags []string `json:"tags"` 73 } 74 75 // JSONVtctldConfig contains a host and tag information for a single 76 // Vtctld in a cluster. 77 type JSONVtctldConfig struct { 78 Host *vtadminpb.Vtctld `json:"host"` 79 Tags []string `json:"tags"` 80 } 81 82 func (d *JSONDiscovery) parseConfig(bytes []byte) error { 83 if err := json.Unmarshal(bytes, &d.config); err != nil { 84 return fmt.Errorf("failed to unmarshal staticfile config from json: %w", err) 85 } 86 87 d.gates.byName = make(map[string]*vtadminpb.VTGate, len(d.config.VTGates)) 88 d.gates.byTag = make(map[string][]*vtadminpb.VTGate) 89 90 // Index the gates by name and by tag for easier lookups 91 for _, gate := range d.config.VTGates { 92 d.gates.byName[gate.Host.Hostname] = gate.Host 93 94 for _, tag := range gate.Tags { 95 d.gates.byTag[tag] = append(d.gates.byTag[tag], gate.Host) 96 } 97 } 98 99 d.vtctlds.byName = make(map[string]*vtadminpb.Vtctld, len(d.config.Vtctlds)) 100 d.vtctlds.byTag = make(map[string][]*vtadminpb.Vtctld) 101 102 // Index the vtctlds by name and by tag for easier lookups 103 for _, vtctld := range d.config.Vtctlds { 104 d.vtctlds.byName[vtctld.Host.Hostname] = vtctld.Host 105 106 for _, tag := range vtctld.Tags { 107 d.vtctlds.byTag[tag] = append(d.vtctlds.byTag[tag], vtctld.Host) 108 } 109 } 110 111 return nil 112 } 113 114 // DiscoverVTGate is part of the Discovery interface. 115 func (d *JSONDiscovery) DiscoverVTGate(ctx context.Context, tags []string) (*vtadminpb.VTGate, error) { 116 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVTGate") 117 defer span.Finish() 118 119 return d.discoverVTGate(ctx, tags) 120 } 121 122 func (d *JSONDiscovery) discoverVTGate(ctx context.Context, tags []string) (*vtadminpb.VTGate, error) { 123 gates, err := d.discoverVTGates(ctx, tags) 124 if err != nil { 125 return nil, err 126 } 127 128 count := len(gates) 129 if count == 0 { 130 return nil, ErrNoVTGates 131 } 132 133 gate := gates[rand.Intn(len(gates))] 134 return gate, nil 135 } 136 137 // DiscoverVTGateAddr is part of the Discovery interface. 138 func (d *JSONDiscovery) DiscoverVTGateAddr(ctx context.Context, tags []string) (string, error) { 139 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVTGateAddr") 140 defer span.Finish() 141 142 gate, err := d.DiscoverVTGate(ctx, tags) 143 if err != nil { 144 return "", err 145 } 146 147 return gate.Hostname, nil 148 } 149 150 // DiscoverVTGateAddrs is part of the Discovery interface. 151 func (d *JSONDiscovery) DiscoverVTGateAddrs(ctx context.Context, tags []string) ([]string, error) { 152 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVTGateAddrs") 153 defer span.Finish() 154 155 gates, err := d.discoverVTGates(ctx, tags) 156 if err != nil { 157 return nil, err 158 } 159 160 addrs := make([]string, len(gates)) 161 for i, gate := range gates { 162 addrs[i] = gate.Hostname 163 } 164 165 return addrs, nil 166 } 167 168 // DiscoverVTGates is part of the Discovery interface. 169 func (d *JSONDiscovery) DiscoverVTGates(ctx context.Context, tags []string) ([]*vtadminpb.VTGate, error) { 170 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVTGates") 171 defer span.Finish() 172 173 return d.discoverVTGates(ctx, tags) 174 } 175 176 func (d *JSONDiscovery) discoverVTGates(ctx context.Context, tags []string) ([]*vtadminpb.VTGate, error) { 177 if len(tags) == 0 { 178 results := []*vtadminpb.VTGate{} 179 for _, g := range d.gates.byName { 180 results = append(results, g) 181 } 182 183 return results, nil 184 } 185 186 set := d.gates.byName 187 188 for _, tag := range tags { 189 intermediate := map[string]*vtadminpb.VTGate{} 190 191 gates, ok := d.gates.byTag[tag] 192 if !ok { 193 return []*vtadminpb.VTGate{}, nil 194 } 195 196 for _, g := range gates { 197 if _, ok := set[g.Hostname]; ok { 198 intermediate[g.Hostname] = g 199 } 200 } 201 202 set = intermediate 203 } 204 205 results := make([]*vtadminpb.VTGate, 0, len(set)) 206 207 for _, gate := range set { 208 results = append(results, gate) 209 } 210 211 return results, nil 212 } 213 214 // DiscoverVtctld is part of the Discovery interface. 215 func (d *JSONDiscovery) DiscoverVtctld(ctx context.Context, tags []string) (*vtadminpb.Vtctld, error) { 216 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVtctld") 217 defer span.Finish() 218 219 return d.discoverVtctld(ctx, tags) 220 } 221 222 func (d *JSONDiscovery) discoverVtctld(ctx context.Context, tags []string) (*vtadminpb.Vtctld, error) { 223 vtctlds, err := d.discoverVtctlds(ctx, tags) 224 if err != nil { 225 return nil, err 226 } 227 228 count := len(vtctlds) 229 if count == 0 { 230 return nil, ErrNoVtctlds 231 } 232 233 vtctld := vtctlds[rand.Intn(len(vtctlds))] 234 return vtctld, nil 235 } 236 237 // DiscoverVtctldAddr is part of the Discovery interface. 238 func (d *JSONDiscovery) DiscoverVtctldAddr(ctx context.Context, tags []string) (string, error) { 239 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVtctldAddr") 240 defer span.Finish() 241 242 vtctld, err := d.discoverVtctld(ctx, tags) 243 if err != nil { 244 return "", err 245 } 246 247 return vtctld.Hostname, nil 248 } 249 250 // DiscoverVtctldAddrs is part of the Discovery interface. 251 func (d *JSONDiscovery) DiscoverVtctldAddrs(ctx context.Context, tags []string) ([]string, error) { 252 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVtctldAddrs") 253 defer span.Finish() 254 255 vtctlds, err := d.discoverVtctlds(ctx, tags) 256 if err != nil { 257 return nil, err 258 } 259 260 addrs := make([]string, len(vtctlds)) 261 for i, vtctld := range vtctlds { 262 addrs[i] = vtctld.Hostname 263 } 264 265 return addrs, nil 266 } 267 268 // DiscoverVtctlds is part of the Discovery interface. 269 func (d *JSONDiscovery) DiscoverVtctlds(ctx context.Context, tags []string) ([]*vtadminpb.Vtctld, error) { 270 span, ctx := trace.NewSpan(ctx, "JSONDiscovery.DiscoverVtctlds") 271 defer span.Finish() 272 273 return d.discoverVtctlds(ctx, tags) 274 } 275 276 func (d *JSONDiscovery) discoverVtctlds(ctx context.Context, tags []string) ([]*vtadminpb.Vtctld, error) { 277 if len(tags) == 0 { 278 results := []*vtadminpb.Vtctld{} 279 for _, v := range d.vtctlds.byName { 280 results = append(results, v) 281 } 282 283 return results, nil 284 } 285 286 set := d.vtctlds.byName 287 288 for _, tag := range tags { 289 intermediate := map[string]*vtadminpb.Vtctld{} 290 291 vtctlds, ok := d.vtctlds.byTag[tag] 292 if !ok { 293 return []*vtadminpb.Vtctld{}, nil 294 } 295 296 for _, v := range vtctlds { 297 if _, ok := set[v.Hostname]; ok { 298 intermediate[v.Hostname] = v 299 } 300 } 301 302 set = intermediate 303 } 304 305 results := make([]*vtadminpb.Vtctld, 0, len(set)) 306 307 for _, vtctld := range set { 308 results = append(results, vtctld) 309 } 310 311 return results, nil 312 }