github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/testutils/serverutils/test_server_shim.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 // 11 // This file provides generic interfaces that allow tests to set up test servers 12 // without importing the server package (avoiding circular dependencies). 13 // To be used, the binary needs to call 14 // InitTestServerFactory(server.TestServerFactory), generally from a TestMain() 15 // in an "foo_test" package (which can import server and is linked together with 16 // the other tests in package "foo"). 17 18 package serverutils 19 20 import ( 21 "context" 22 gosql "database/sql" 23 "net/http" 24 "net/url" 25 "testing" 26 27 "github.com/cockroachdb/cockroach/pkg/base" 28 "github.com/cockroachdb/cockroach/pkg/kv" 29 "github.com/cockroachdb/cockroach/pkg/roachpb" 30 "github.com/cockroachdb/cockroach/pkg/rpc" 31 "github.com/cockroachdb/cockroach/pkg/security" 32 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 33 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 34 "github.com/cockroachdb/cockroach/pkg/util/hlc" 35 "github.com/cockroachdb/cockroach/pkg/util/httputil" 36 "github.com/cockroachdb/cockroach/pkg/util/protoutil" 37 "github.com/cockroachdb/cockroach/pkg/util/stop" 38 ) 39 40 // TestServerInterface defines test server functionality that tests need; it is 41 // implemented by server.TestServer. 42 type TestServerInterface interface { 43 Stopper() *stop.Stopper 44 45 Start(params base.TestServerArgs) error 46 47 // Node returns the server.Node as an interface{}. 48 Node() interface{} 49 50 // NodeID returns the ID of this node within its cluster. 51 NodeID() roachpb.NodeID 52 53 // ServingRPCAddr returns the server's advertised address. 54 ServingRPCAddr() string 55 56 // ServingSQLAddr returns the server's advertised SQL address. 57 ServingSQLAddr() string 58 59 // HTTPAddr returns the server's http address. 60 HTTPAddr() string 61 62 // RPCAddr returns the server's RPC address. 63 // Note: use ServingRPCAddr() instead unless specific reason not to. 64 RPCAddr() string 65 66 // SQLAddr returns the server's SQL address. 67 // Note: use ServingSQLAddr() instead unless specific reason not to. 68 SQLAddr() string 69 70 // DB returns a *client.DB instance for talking to this KV server. 71 DB() *kv.DB 72 73 // RPCContext returns the rpc context used by the test server. 74 RPCContext() *rpc.Context 75 76 // LeaseManager() returns the *sql.LeaseManager as an interface{}. 77 LeaseManager() interface{} 78 79 // InternalExecutor returns a *sql.InternalExecutor as an interface{} (which 80 // also implements sqlutil.InternalExecutor if the test cannot depend on sql). 81 InternalExecutor() interface{} 82 83 // ExecutorConfig returns a copy of the server's ExecutorConfig. 84 // The real return type is sql.ExecutorConfig. 85 ExecutorConfig() interface{} 86 87 // GossipI returns the gossip used by the TestServer. 88 // The real return type is *gossip.Gossip. 89 GossipI() interface{} 90 91 // Clock returns the clock used by the TestServer. 92 Clock() *hlc.Clock 93 94 // DistSenderI returns the DistSender used by the TestServer. 95 // The real return type is *kv.DistSender. 96 DistSenderI() interface{} 97 98 // SQLServer returns the *sql.Server as an interface{}. 99 SQLServer() interface{} 100 101 // DistSQLServer returns the *distsql.ServerImpl as an interface{}. 102 DistSQLServer() interface{} 103 104 // JobRegistry returns the *jobs.Registry as an interface{}. 105 JobRegistry() interface{} 106 107 // MigrationManager returns the *jobs.Registry as an interface{}. 108 MigrationManager() interface{} 109 110 // SetDistSQLSpanResolver changes the SpanResolver used for DistSQL inside the 111 // server's executor. The argument must be a physicalplan.SpanResolver 112 // instance. 113 // 114 // This method exists because we cannot pass the fake span resolver with the 115 // server or cluster params: the fake span resolver needs the node IDs and 116 // addresses of the servers in a cluster, which are not available before we 117 // start the servers. 118 // 119 // It is the caller's responsibility to make sure no queries are being run 120 // with DistSQL at the same time. 121 SetDistSQLSpanResolver(spanResolver interface{}) 122 123 // AdminURL returns the URL for the admin UI. 124 AdminURL() string 125 // GetHTTPClient returns an http client configured with the client TLS 126 // config required by the TestServer's configuration. 127 GetHTTPClient() (http.Client, error) 128 // GetAdminAuthenticatedHTTPClient returns an http client which has been 129 // authenticated to access Admin API methods (via a cookie). 130 // The user has admin privileges. 131 GetAdminAuthenticatedHTTPClient() (http.Client, error) 132 // GetAuthenticatedHTTPClient returns an http client which has been 133 // authenticated to access Admin API methods (via a cookie). 134 GetAuthenticatedHTTPClient(isAdmin bool) (http.Client, error) 135 136 // MustGetSQLCounter returns the value of a counter metric from the server's 137 // SQL Executor. Runs in O(# of metrics) time, which is fine for test code. 138 MustGetSQLCounter(name string) int64 139 // MustGetSQLNetworkCounter returns the value of a counter metric from the 140 // server's SQL server. Runs in O(# of metrics) time, which is fine for test 141 // code. 142 MustGetSQLNetworkCounter(name string) int64 143 // WriteSummaries records summaries of time-series data, which is required for 144 // any tests that query server stats. 145 WriteSummaries() error 146 147 // GetFirstStoreID is a utility function returning the StoreID of the first 148 // store on this node. 149 GetFirstStoreID() roachpb.StoreID 150 151 // GetStores returns the collection of stores from this TestServer's node. 152 // The return value is of type *storage.Stores. 153 GetStores() interface{} 154 155 // ClusterSettings returns the ClusterSettings shared by all components of 156 // this server. 157 ClusterSettings() *cluster.Settings 158 159 // SplitRange splits the range containing splitKey. 160 SplitRange( 161 splitKey roachpb.Key, 162 ) (left roachpb.RangeDescriptor, right roachpb.RangeDescriptor, err error) 163 164 // MergeRanges merges the range containing leftKey with the following adjacent 165 // range. 166 MergeRanges(leftKey roachpb.Key) (merged roachpb.RangeDescriptor, err error) 167 168 // ExpectedInitialRangeCount returns the expected number of ranges that should 169 // be on the server after initial (asynchronous) splits have been completed, 170 // assuming no additional information is added outside of the normal bootstrap 171 // process. 172 ExpectedInitialRangeCount() (int, error) 173 174 // ForceTableGC sends a GCRequest for the ranges corresponding to a table. 175 // 176 // An error will be returned if the same table name exists in multiple schemas 177 // inside the specified database. 178 ForceTableGC(ctx context.Context, database, table string, timestamp hlc.Timestamp) error 179 180 // CheckForUpdates phones home to check for updates and report usage. 181 // 182 // When using this for testing, consider setting DiagnosticsReportingEnabled 183 // to false so the periodic check doesn't interfere with the test. 184 // 185 // This can be slow because of cloud detection; use cloudinfo.Disable() in 186 // tests to avoid that. 187 CheckForUpdates(ctx context.Context) 188 189 // ReportDiagnostics phones home to report diagnostics. 190 // 191 // If using this for testing, consider setting DiagnosticsReportingEnabled to 192 // false so the periodic reporting doesn't interfere with the test. 193 // 194 // This can be slow because of cloud detection; use cloudinfo.Disable() in 195 // tests to avoid that. 196 ReportDiagnostics(ctx context.Context) 197 198 // StartTenant spawns off tenant process connecting to this TestServer. 199 StartTenant(params base.TestTenantArgs) (pgAddr string, _ error) 200 } 201 202 // TestServerFactory encompasses the actual implementation of the shim 203 // service. 204 type TestServerFactory interface { 205 // New instantiates a test server. 206 New(params base.TestServerArgs) interface{} 207 } 208 209 var srvFactoryImpl TestServerFactory 210 211 // InitTestServerFactory should be called once to provide the implementation 212 // of the service. It will be called from a xx_test package that can import the 213 // server package. 214 func InitTestServerFactory(impl TestServerFactory) { 215 srvFactoryImpl = impl 216 } 217 218 // StartServer creates a test server and sets up a gosql DB connection. 219 // The server should be stopped by calling server.Stopper().Stop(). 220 func StartServer( 221 t testing.TB, params base.TestServerArgs, 222 ) (TestServerInterface, *gosql.DB, *kv.DB) { 223 server, err := StartServerRaw(params) 224 if err != nil { 225 t.Fatalf("%+v", err) 226 } 227 228 pgURL, cleanupGoDB := sqlutils.PGUrl( 229 t, server.ServingSQLAddr(), "StartServer" /* prefix */, url.User(security.RootUser)) 230 pgURL.Path = params.UseDatabase 231 if params.Insecure { 232 pgURL.RawQuery = "sslmode=disable" 233 } 234 goDB, err := gosql.Open("postgres", pgURL.String()) 235 if err != nil { 236 t.Fatal(err) 237 } 238 server.Stopper().AddCloser( 239 stop.CloserFn(func() { 240 _ = goDB.Close() 241 cleanupGoDB() 242 })) 243 return server, goDB, server.DB() 244 } 245 246 // StartServerRaw creates and starts a TestServer. 247 // Generally StartServer() should be used. However this function can be used 248 // directly when opening a connection to the server is not desired. 249 func StartServerRaw(args base.TestServerArgs) (TestServerInterface, error) { 250 if srvFactoryImpl == nil { 251 panic("TestServerFactory not initialized. One needs to be injected " + 252 "from the package's TestMain()") 253 } 254 server := srvFactoryImpl.New(args).(TestServerInterface) 255 if err := server.Start(args); err != nil { 256 return nil, err 257 } 258 return server, nil 259 } 260 261 // StartTenant starts a tenant SQL server connecting to the supplied test 262 // server. It uses the server's stopper to shut down automatically. However, 263 // the returned DB is for the caller to close. 264 func StartTenant(t testing.TB, ts TestServerInterface, params base.TestTenantArgs) *gosql.DB { 265 pgAddr, err := ts.StartTenant(params) 266 if err != nil { 267 t.Fatal(err) 268 } 269 270 pgURL, cleanupGoDB := sqlutils.PGUrl( 271 t, pgAddr, t.Name() /* prefix */, url.User(security.RootUser)) 272 273 db, err := gosql.Open("postgres", pgURL.String()) 274 if err != nil { 275 t.Fatal(err) 276 } 277 ts.Stopper().AddCloser(stop.CloserFn(func() { 278 cleanupGoDB() 279 })) 280 return db 281 } 282 283 // GetJSONProto uses the supplied client to GET the URL specified by the parameters 284 // and unmarshals the result into response. 285 func GetJSONProto(ts TestServerInterface, path string, response protoutil.Message) error { 286 return GetJSONProtoWithAdminOption(ts, path, response, true) 287 } 288 289 // GetJSONProtoWithAdminOption is like GetJSONProto but the caller can customize 290 // whether the request is performed with admin privilege 291 func GetJSONProtoWithAdminOption( 292 ts TestServerInterface, path string, response protoutil.Message, isAdmin bool, 293 ) error { 294 httpClient, err := ts.GetAuthenticatedHTTPClient(isAdmin) 295 if err != nil { 296 return err 297 } 298 return httputil.GetJSON(httpClient, ts.AdminURL()+path, response) 299 } 300 301 // PostJSONProto uses the supplied client to POST the URL specified by the parameters 302 // and unmarshals the result into response. 303 func PostJSONProto(ts TestServerInterface, path string, request, response protoutil.Message) error { 304 return PostJSONProtoWithAdminOption(ts, path, request, response, true) 305 } 306 307 // PostJSONProtoWithAdminOption is like PostJSONProto but the caller 308 // can customize whether the request is performed with admin 309 // privilege. 310 func PostJSONProtoWithAdminOption( 311 ts TestServerInterface, path string, request, response protoutil.Message, isAdmin bool, 312 ) error { 313 httpClient, err := ts.GetAuthenticatedHTTPClient(isAdmin) 314 if err != nil { 315 return err 316 } 317 return httputil.PostJSON(httpClient, ts.AdminURL()+path, request, response) 318 }