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  }