vitess.io/vitess@v0.16.2/go/vt/vtadmin/testutil/cluster.go (about) 1 /* 2 Copyright 2021 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 testutil 18 19 import ( 20 "context" 21 "database/sql" 22 "fmt" 23 "sync" 24 "testing" 25 26 "github.com/spf13/pflag" 27 "github.com/stretchr/testify/require" 28 "google.golang.org/grpc" 29 30 "vitess.io/vitess/go/vt/grpcclient" 31 "vitess.io/vitess/go/vt/topo" 32 "vitess.io/vitess/go/vt/topo/memorytopo" 33 "vitess.io/vitess/go/vt/vitessdriver" 34 "vitess.io/vitess/go/vt/vtadmin/cluster" 35 "vitess.io/vitess/go/vt/vtadmin/cluster/discovery" 36 "vitess.io/vitess/go/vt/vtadmin/cluster/discovery/fakediscovery" 37 vtadminvtctldclient "vitess.io/vitess/go/vt/vtadmin/vtctldclient" 38 "vitess.io/vitess/go/vt/vtadmin/vtsql" 39 "vitess.io/vitess/go/vt/vtadmin/vtsql/fakevtsql" 40 "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" 41 grpcvtctldtestutil "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" 42 "vitess.io/vitess/go/vt/vtctl/localvtctldclient" 43 "vitess.io/vitess/go/vt/vtctl/vtctldclient" 44 45 vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" 46 vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" 47 ) 48 49 // Dbcfg is a test utility for controlling the behavior of the cluster's DB 50 // at the package sql level. 51 type Dbcfg struct { 52 ShouldErr bool 53 } 54 55 // TestClusterConfig controls the way that a cluster.Cluster object is 56 // constructed for testing vtadmin code. 57 type TestClusterConfig struct { 58 // Cluster provides the protobuf-based version of the cluster info. It is 59 // to set the ID and Name of the resulting cluster.Cluster, as well as to 60 // name a single, phony, vtgate entry in the cluster's discovery service. 61 Cluster *vtadminpb.Cluster 62 // VtctldClient provides the vtctldclient.VtctldClient implementation the 63 // cluster's vtctld proxy will use. Most unit tests will use an instance of 64 // the VtctldClient type provided by this package in order to mock out the 65 // vtctld layer. 66 VtctldClient vtctldclient.VtctldClient 67 // Tablets provides the set of tablets reachable by this cluster's vtsql.DB. 68 // Tablets are copied, and then mutated to have their Cluster field set to 69 // match the Cluster provided by this TestClusterConfig, so mutations are 70 // transparent to the caller. 71 Tablets []*vtadminpb.Tablet 72 // DBConfig controls the behavior of the cluster's vtsql.DB. 73 DBConfig Dbcfg 74 // Config controls certain cluster config options, primarily used to 75 // properly setup various RPC pools for different testing scenarios. 76 // Other fields (such as ID, Name, and DiscoveryImpl) are ignored. 77 Config *cluster.Config 78 } 79 80 const discoveryTestImplName = "vtadmin.testutil" 81 82 var ( 83 m sync.Mutex 84 testdisco discovery.Discovery 85 ) 86 87 func init() { 88 discovery.Register(discoveryTestImplName, func(cluster *vtadminpb.Cluster, flags *pflag.FlagSet, args []string) (discovery.Discovery, error) { 89 return testdisco, nil 90 }) 91 } 92 93 // BuildCluster is a shared helper for building a cluster based on the given 94 // test configuration. 95 func BuildCluster(t testing.TB, cfg TestClusterConfig) *cluster.Cluster { 96 t.Helper() 97 98 disco := fakediscovery.New() 99 disco.AddTaggedGates(nil, &vtadminpb.VTGate{Hostname: fmt.Sprintf("%s-%s-gate", cfg.Cluster.Name, cfg.Cluster.Id)}) 100 disco.AddTaggedVtctlds(nil, &vtadminpb.Vtctld{Hostname: "doesn't matter"}) 101 102 tablets := make([]*vtadminpb.Tablet, len(cfg.Tablets)) 103 for i, t := range cfg.Tablets { 104 tablet := &vtadminpb.Tablet{ 105 Cluster: cfg.Cluster, 106 Tablet: t.Tablet, 107 State: t.State, 108 } 109 110 tablets[i] = tablet 111 } 112 113 var clusterConf cluster.Config 114 if cfg.Config != nil { 115 clusterConf = *cfg.Config 116 } 117 118 clusterConf.ID = cfg.Cluster.Id 119 clusterConf.Name = cfg.Cluster.Name 120 clusterConf.DiscoveryImpl = discoveryTestImplName 121 122 clusterConf = clusterConf.WithVtctldTestConfigOptions(vtadminvtctldclient.WithDialFunc(func(addr string, ff grpcclient.FailFast, opts ...grpc.DialOption) (vtctldclient.VtctldClient, error) { 123 return cfg.VtctldClient, nil 124 })).WithVtSQLTestConfigOptions(vtsql.WithDialFunc(func(c vitessdriver.Configuration) (*sql.DB, error) { 125 return sql.OpenDB(&fakevtsql.Connector{Tablets: tablets, ShouldErr: cfg.DBConfig.ShouldErr}), nil 126 })) 127 128 m.Lock() 129 testdisco = disco 130 c, err := cluster.New( 131 context.Background(), // consider updating this function to allow callers to provide a context. 132 clusterConf, 133 ) 134 m.Unlock() 135 136 require.NoError(t, err, "failed to create cluster from configs %+v %+v", clusterConf, cfg) 137 138 return c 139 } 140 141 // BuildClusters is a helper for building multiple clusters from a slice of 142 // TestClusterConfigs. 143 func BuildClusters(t testing.TB, cfgs ...TestClusterConfig) []*cluster.Cluster { 144 clusters := make([]*cluster.Cluster, len(cfgs)) 145 146 for i, cfg := range cfgs { 147 clusters[i] = BuildCluster(t, cfg) 148 } 149 150 return clusters 151 } 152 153 // IntegrationTestCluster is a vtadmin cluster suitable for use in integration 154 // tests. It contains the cluster struct, the topo server backing the cluster, 155 // and the memorytopo.Factory to force topo errors for certain test cases. 156 type IntegrationTestCluster struct { 157 Cluster *cluster.Cluster 158 Topo *topo.Server 159 TopoFactory *memorytopo.Factory 160 } 161 162 // BuildIntegrationTestCluster is a helper for building a test cluster with a 163 // real grpcvtctldserver-backing implementation. 164 // 165 // (TODO|@ajm188): Unify this with the BuildCluster API. Also this does not 166 // support any cluster methods that involve vtgate/vitessdriver queries. 167 func BuildIntegrationTestCluster(t testing.TB, c *vtadminpb.Cluster, cells ...string) *IntegrationTestCluster { 168 t.Helper() 169 170 ts, factory := memorytopo.NewServerAndFactory(cells...) 171 vtctld := grpcvtctldtestutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 172 return grpcvtctldserver.NewVtctldServer(ts) 173 }) 174 175 localclient := localvtctldclient.New(vtctld) 176 177 testcluster := BuildCluster(t, TestClusterConfig{ 178 Cluster: c, 179 VtctldClient: localclient, 180 }) 181 return &IntegrationTestCluster{ 182 Cluster: testcluster, 183 Topo: ts, 184 TopoFactory: factory, 185 } 186 }