github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/server/admin_test.go (about)

     1  // Copyright 2014 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  package server
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	"encoding/json"
    17  	"fmt"
    18  	"io/ioutil"
    19  	"math"
    20  	"net/http"
    21  	"net/url"
    22  	"reflect"
    23  	"regexp"
    24  	"sort"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/cockroachdb/cockroach/pkg/base"
    30  	"github.com/cockroachdb/cockroach/pkg/config/zonepb"
    31  	"github.com/cockroachdb/cockroach/pkg/jobs"
    32  	"github.com/cockroachdb/cockroach/pkg/jobs/jobspb"
    33  	"github.com/cockroachdb/cockroach/pkg/keys"
    34  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver"
    35  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb"
    36  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    37  	"github.com/cockroachdb/cockroach/pkg/security"
    38  	"github.com/cockroachdb/cockroach/pkg/server/debug"
    39  	"github.com/cockroachdb/cockroach/pkg/server/serverpb"
    40  	"github.com/cockroachdb/cockroach/pkg/settings"
    41  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    42  	"github.com/cockroachdb/cockroach/pkg/sql"
    43  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    44  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    45  	"github.com/cockroachdb/cockroach/pkg/testutils"
    46  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    47  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    48  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    49  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    50  	"github.com/cockroachdb/cockroach/pkg/util/log"
    51  	"github.com/cockroachdb/cockroach/pkg/util/protoutil"
    52  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    53  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    54  	"github.com/cockroachdb/errors"
    55  	"github.com/gogo/protobuf/proto"
    56  	"github.com/stretchr/testify/assert"
    57  	"github.com/stretchr/testify/require"
    58  )
    59  
    60  func getAdminJSONProto(
    61  	ts serverutils.TestServerInterface, path string, response protoutil.Message,
    62  ) error {
    63  	return getAdminJSONProtoWithAdminOption(ts, path, response, true)
    64  }
    65  
    66  func getAdminJSONProtoWithAdminOption(
    67  	ts serverutils.TestServerInterface, path string, response protoutil.Message, isAdmin bool,
    68  ) error {
    69  	return serverutils.GetJSONProtoWithAdminOption(ts, adminPrefix+path, response, isAdmin)
    70  }
    71  
    72  func postAdminJSONProto(
    73  	ts serverutils.TestServerInterface, path string, request, response protoutil.Message,
    74  ) error {
    75  	return postAdminJSONProtoWithAdminOption(ts, path, request, response, true)
    76  }
    77  
    78  func postAdminJSONProtoWithAdminOption(
    79  	ts serverutils.TestServerInterface,
    80  	path string,
    81  	request, response protoutil.Message,
    82  	isAdmin bool,
    83  ) error {
    84  	return serverutils.PostJSONProtoWithAdminOption(ts, adminPrefix+path, request, response, isAdmin)
    85  }
    86  
    87  // getText fetches the HTTP response body as text in the form of a
    88  // byte slice from the specified URL.
    89  func getText(ts serverutils.TestServerInterface, url string) ([]byte, error) {
    90  	httpClient, err := ts.GetAdminAuthenticatedHTTPClient()
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	resp, err := httpClient.Get(url)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	defer resp.Body.Close()
    99  	return ioutil.ReadAll(resp.Body)
   100  }
   101  
   102  // getJSON fetches the JSON from the specified URL and returns
   103  // it as unmarshaled JSON. Returns an error on any failure to fetch
   104  // or unmarshal response body.
   105  func getJSON(ts serverutils.TestServerInterface, url string) (interface{}, error) {
   106  	body, err := getText(ts, url)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	var jI interface{}
   111  	if err := json.Unmarshal(body, &jI); err != nil {
   112  		return nil, errors.Wrapf(err, "body is:\n%s", body)
   113  	}
   114  	return jI, nil
   115  }
   116  
   117  // debugURL returns the root debug URL.
   118  func debugURL(s serverutils.TestServerInterface) string {
   119  	return s.AdminURL() + debug.Endpoint
   120  }
   121  
   122  // TestAdminDebugExpVar verifies that cmdline and memstats variables are
   123  // available via the /debug/vars link.
   124  func TestAdminDebugExpVar(t *testing.T) {
   125  	defer leaktest.AfterTest(t)()
   126  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   127  	defer s.Stopper().Stop(context.Background())
   128  
   129  	jI, err := getJSON(s, debugURL(s)+"vars")
   130  	if err != nil {
   131  		t.Fatalf("failed to fetch JSON: %v", err)
   132  	}
   133  	j := jI.(map[string]interface{})
   134  	if _, ok := j["cmdline"]; !ok {
   135  		t.Error("cmdline not found in JSON response")
   136  	}
   137  	if _, ok := j["memstats"]; !ok {
   138  		t.Error("memstats not found in JSON response")
   139  	}
   140  }
   141  
   142  // TestAdminDebugMetrics verifies that cmdline and memstats variables are
   143  // available via the /debug/metrics link.
   144  func TestAdminDebugMetrics(t *testing.T) {
   145  	defer leaktest.AfterTest(t)()
   146  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   147  	defer s.Stopper().Stop(context.Background())
   148  
   149  	jI, err := getJSON(s, debugURL(s)+"metrics")
   150  	if err != nil {
   151  		t.Fatalf("failed to fetch JSON: %v", err)
   152  	}
   153  	j := jI.(map[string]interface{})
   154  	if _, ok := j["cmdline"]; !ok {
   155  		t.Error("cmdline not found in JSON response")
   156  	}
   157  	if _, ok := j["memstats"]; !ok {
   158  		t.Error("memstats not found in JSON response")
   159  	}
   160  }
   161  
   162  // TestAdminDebugPprof verifies that pprof tools are available.
   163  // via the /debug/pprof/* links.
   164  func TestAdminDebugPprof(t *testing.T) {
   165  	defer leaktest.AfterTest(t)()
   166  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   167  	defer s.Stopper().Stop(context.Background())
   168  
   169  	body, err := getText(s, debugURL(s)+"pprof/block?debug=1")
   170  	if err != nil {
   171  		t.Fatal(err)
   172  	}
   173  	if exp := "contention:\ncycles/second="; !bytes.Contains(body, []byte(exp)) {
   174  		t.Errorf("expected %s to contain %s", body, exp)
   175  	}
   176  }
   177  
   178  // TestAdminDebugTrace verifies that the net/trace endpoints are available
   179  // via /debug/{requests,events}.
   180  func TestAdminDebugTrace(t *testing.T) {
   181  	defer leaktest.AfterTest(t)()
   182  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   183  	defer s.Stopper().Stop(context.Background())
   184  
   185  	tc := []struct {
   186  		segment, search string
   187  	}{
   188  		{"requests", "<title>/debug/requests</title>"},
   189  		{"events", "<title>events</title>"},
   190  	}
   191  
   192  	for _, c := range tc {
   193  		body, err := getText(s, debugURL(s)+c.segment)
   194  		if err != nil {
   195  			t.Fatal(err)
   196  		}
   197  		if !bytes.Contains(body, []byte(c.search)) {
   198  			t.Errorf("expected %s to be contained in %s", c.search, body)
   199  		}
   200  	}
   201  }
   202  
   203  // TestAdminDebugRedirect verifies that the /debug/ endpoint is redirected to on
   204  // incorrect /debug/ paths.
   205  func TestAdminDebugRedirect(t *testing.T) {
   206  	defer leaktest.AfterTest(t)()
   207  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   208  	defer s.Stopper().Stop(context.Background())
   209  
   210  	expURL := debugURL(s)
   211  	origURL := expURL + "incorrect"
   212  
   213  	// There are no particular permissions on admin endpoints, TestUser is fine.
   214  	client, err := testutils.NewTestBaseContext(TestUser).GetHTTPClient()
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  
   219  	// Don't follow redirects automatically.
   220  	redirectAttemptedError := errors.New("redirect")
   221  	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
   222  		return redirectAttemptedError
   223  	}
   224  
   225  	resp, err := client.Get(origURL)
   226  	if urlError := (*url.Error)(nil); errors.As(err, &urlError) &&
   227  		errors.Is(urlError.Err, redirectAttemptedError) {
   228  		// Ignore the redirectAttemptedError.
   229  		err = nil
   230  	}
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	} else {
   234  		resp.Body.Close()
   235  		if resp.StatusCode != http.StatusMovedPermanently {
   236  			t.Errorf("expected status code %d; got %d", http.StatusMovedPermanently, resp.StatusCode)
   237  		}
   238  		if redirectURL, err := resp.Location(); err != nil {
   239  			t.Error(err)
   240  		} else if foundURL := redirectURL.String(); foundURL != expURL {
   241  			t.Errorf("expected location %s; got %s", expURL, foundURL)
   242  		}
   243  	}
   244  }
   245  
   246  func TestAdminAPIDatabases(t *testing.T) {
   247  	defer leaktest.AfterTest(t)()
   248  	s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
   249  	defer s.Stopper().Stop(context.Background())
   250  	ts := s.(*TestServer)
   251  
   252  	ac := log.AmbientContext{Tracer: s.ClusterSettings().Tracer}
   253  	ctx, span := ac.AnnotateCtxWithSpan(context.Background(), "test")
   254  	defer span.Finish()
   255  
   256  	const testdb = "test"
   257  	query := "CREATE DATABASE " + testdb
   258  	if _, err := db.Exec(query); err != nil {
   259  		t.Fatal(err)
   260  	}
   261  
   262  	// We have to create the non-admin user before calling
   263  	// "GRANT ... TO authenticatedUserNameNoAdmin".
   264  	// This is done in "GetAuthenticatedHTTPClient".
   265  	if _, err := ts.GetAuthenticatedHTTPClient(false); err != nil {
   266  		t.Fatal(err)
   267  	}
   268  
   269  	// Grant permissions to view the tables for the given viewing user.
   270  	privileges := []string{"SELECT", "UPDATE"}
   271  	query = fmt.Sprintf(
   272  		"GRANT %s ON DATABASE %s TO %s",
   273  		strings.Join(privileges, ", "),
   274  		testdb,
   275  		authenticatedUserNameNoAdmin,
   276  	)
   277  	if _, err := db.Exec(query); err != nil {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	for _, tc := range []struct {
   282  		expectedDBs []string
   283  		isAdmin     bool
   284  	}{
   285  		{[]string{"defaultdb", "postgres", "system", testdb}, true},
   286  		{[]string{testdb}, false},
   287  	} {
   288  		t.Run(fmt.Sprintf("isAdmin:%t", tc.isAdmin), func(t *testing.T) {
   289  			// Test databases endpoint.
   290  			var resp serverpb.DatabasesResponse
   291  			if err := getAdminJSONProtoWithAdminOption(
   292  				s,
   293  				"databases",
   294  				&resp,
   295  				tc.isAdmin,
   296  			); err != nil {
   297  				t.Fatal(err)
   298  			}
   299  
   300  			if a, e := len(resp.Databases), len(tc.expectedDBs); a != e {
   301  				t.Fatalf("length of result %d != expected %d", a, e)
   302  			}
   303  
   304  			sort.Strings(resp.Databases)
   305  			for i, e := range tc.expectedDBs {
   306  				if a := resp.Databases[i]; a != e {
   307  					t.Fatalf("database name %s != expected %s", a, e)
   308  				}
   309  			}
   310  
   311  			// Test database details endpoint.
   312  			var details serverpb.DatabaseDetailsResponse
   313  			if err := getAdminJSONProtoWithAdminOption(
   314  				s,
   315  				"databases/"+testdb,
   316  				&details,
   317  				tc.isAdmin,
   318  			); err != nil {
   319  				t.Fatal(err)
   320  			}
   321  
   322  			if a, e := len(details.Grants), 4; a != e {
   323  				t.Fatalf("# of grants %d != expected %d", a, e)
   324  			}
   325  
   326  			userGrants := make(map[string][]string)
   327  			for _, grant := range details.Grants {
   328  				switch grant.User {
   329  				case sqlbase.AdminRole, security.RootUser, authenticatedUserNameNoAdmin:
   330  					userGrants[grant.User] = append(userGrants[grant.User], grant.Privileges...)
   331  				default:
   332  					t.Fatalf("unknown grant to user %s", grant.User)
   333  				}
   334  			}
   335  			for u, p := range userGrants {
   336  				switch u {
   337  				case sqlbase.AdminRole:
   338  					if !reflect.DeepEqual(p, []string{"ALL"}) {
   339  						t.Fatalf("privileges %v != expected %v", p, privileges)
   340  					}
   341  				case security.RootUser:
   342  					if !reflect.DeepEqual(p, []string{"ALL"}) {
   343  						t.Fatalf("privileges %v != expected %v", p, privileges)
   344  					}
   345  				case authenticatedUserNameNoAdmin:
   346  					sort.Strings(p)
   347  					if !reflect.DeepEqual(p, privileges) {
   348  						t.Fatalf("privileges %v != expected %v", p, privileges)
   349  					}
   350  				default:
   351  					t.Fatalf("unknown grant to user %s", u)
   352  				}
   353  			}
   354  
   355  			// Verify Descriptor ID.
   356  			path, err := ts.admin.queryDescriptorIDPath(ctx, security.RootUser, []string{testdb})
   357  			if err != nil {
   358  				t.Fatal(err)
   359  			}
   360  			if a, e := details.DescriptorID, int64(path[1]); a != e {
   361  				t.Fatalf("db had descriptorID %d, expected %d", a, e)
   362  			}
   363  		})
   364  	}
   365  }
   366  
   367  func TestAdminAPIDatabaseDoesNotExist(t *testing.T) {
   368  	defer leaktest.AfterTest(t)()
   369  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   370  	defer s.Stopper().Stop(context.Background())
   371  
   372  	const errPattern = "database.+does not exist"
   373  	if err := getAdminJSONProto(s, "databases/i_do_not_exist", nil); !testutils.IsError(err, errPattern) {
   374  		t.Fatalf("unexpected error: %v\nexpected: %s", err, errPattern)
   375  	}
   376  }
   377  
   378  func TestAdminAPIDatabaseSQLInjection(t *testing.T) {
   379  	defer leaktest.AfterTest(t)()
   380  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   381  	defer s.Stopper().Stop(context.Background())
   382  
   383  	const fakedb = "system;DROP DATABASE system;"
   384  	const path = "databases/" + fakedb
   385  	const errPattern = `target database or schema does not exist`
   386  	if err := getAdminJSONProto(s, path, nil); !testutils.IsError(err, errPattern) {
   387  		t.Fatalf("unexpected error: %v\nexpected: %s", err, errPattern)
   388  	}
   389  }
   390  
   391  func TestAdminAPINonTableStats(t *testing.T) {
   392  	defer leaktest.AfterTest(t)()
   393  	testCluster := serverutils.StartTestCluster(t, 3, base.TestClusterArgs{})
   394  	defer testCluster.Stopper().Stop(context.Background())
   395  	s := testCluster.Server(0)
   396  
   397  	// Skip TableStatsResponse.Stats comparison, since it includes data which
   398  	// aren't consistent (time, bytes).
   399  	expectedResponse := serverpb.NonTableStatsResponse{
   400  		TimeSeriesStats: &serverpb.TableStatsResponse{
   401  			RangeCount:   1,
   402  			ReplicaCount: 3,
   403  			NodeCount:    3,
   404  		},
   405  		InternalUseStats: &serverpb.TableStatsResponse{
   406  			RangeCount:   9,
   407  			ReplicaCount: 12,
   408  			NodeCount:    3,
   409  		},
   410  	}
   411  
   412  	var resp serverpb.NonTableStatsResponse
   413  	if err := getAdminJSONProto(s, "nontablestats", &resp); err != nil {
   414  		t.Fatal(err)
   415  	}
   416  
   417  	assertExpectedStatsResponse := func(expected, actual *serverpb.TableStatsResponse) {
   418  		assert.Equal(t, expected.RangeCount, actual.RangeCount)
   419  		assert.Equal(t, expected.ReplicaCount, actual.ReplicaCount)
   420  		assert.Equal(t, expected.NodeCount, actual.NodeCount)
   421  	}
   422  
   423  	assertExpectedStatsResponse(expectedResponse.TimeSeriesStats, resp.TimeSeriesStats)
   424  	assertExpectedStatsResponse(expectedResponse.InternalUseStats, resp.InternalUseStats)
   425  }
   426  
   427  // Verify that for a cluster with no user data, all the ranges on the Databases
   428  // page consist of:
   429  // 1) the total ranges listed for the system database
   430  // 2) the total ranges listed for the Non-Table data
   431  func TestRangeCount(t *testing.T) {
   432  	defer leaktest.AfterTest(t)()
   433  	testCluster := serverutils.StartTestCluster(t, 3, base.TestClusterArgs{})
   434  	defer testCluster.Stopper().Stop(context.Background())
   435  	s := testCluster.Server(0)
   436  
   437  	// Sum up ranges for non-table parts of the system returned
   438  	// from the "nontablestats" enpoint.
   439  	getNonTableRangeCount := func() (ts, internal int64) {
   440  		var resp serverpb.NonTableStatsResponse
   441  		if err := getAdminJSONProto(s, "nontablestats", &resp); err != nil {
   442  			t.Fatal(err)
   443  		}
   444  		return resp.TimeSeriesStats.RangeCount, resp.InternalUseStats.RangeCount
   445  	}
   446  
   447  	// Return map tablename=>count obtained from the
   448  	// "databases/system/tables/{table}" endpoints.
   449  	getSystemTableRangeCount := func() map[string]int64 {
   450  		m := map[string]int64{}
   451  		var dbResp serverpb.DatabaseDetailsResponse
   452  		if err := getAdminJSONProto(s, "databases/system", &dbResp); err != nil {
   453  			t.Fatal(err)
   454  		}
   455  		for _, tableName := range dbResp.TableNames {
   456  			var tblResp serverpb.TableStatsResponse
   457  			path := "databases/system/tables/" + tableName + "/stats"
   458  			if err := getAdminJSONProto(s, path, &tblResp); err != nil {
   459  				t.Fatal(err)
   460  			}
   461  			m[tableName] = tblResp.RangeCount
   462  		}
   463  		return m
   464  	}
   465  
   466  	getRangeCountFromFullSpan := func() int64 {
   467  		adminServer := s.(*TestServer).Server.admin
   468  		stats, err := adminServer.statsForSpan(context.Background(), roachpb.Span{
   469  			Key:    keys.LocalMax,
   470  			EndKey: keys.MaxKey,
   471  		})
   472  		if err != nil {
   473  			t.Fatal(err)
   474  		}
   475  		return stats.RangeCount
   476  	}
   477  
   478  	exp := getRangeCountFromFullSpan()
   479  
   480  	sysDBMap := getSystemTableRangeCount()
   481  	{
   482  		// The tables below sit on the SystemConfigRange. For technical reason,
   483  		// their range count comes back as zero. Let's just use the descriptor
   484  		// table to count this range as they're not picked up by the "non-table
   485  		// data" neither.
   486  		for _, table := range []string{"descriptor", "settings", "namespace", "zones"} {
   487  			n, ok := sysDBMap[table]
   488  			require.True(t, ok, table)
   489  			require.Zero(t, n, table)
   490  		}
   491  
   492  		sysDBMap["descriptor"] = 1
   493  
   494  	}
   495  	var systemTableRangeCount int64
   496  	for _, n := range sysDBMap {
   497  		systemTableRangeCount += n
   498  	}
   499  
   500  	tsCount, internalCount := getNonTableRangeCount()
   501  
   502  	act := tsCount + internalCount + systemTableRangeCount
   503  
   504  	if !assert.Equal(t,
   505  		exp,
   506  		act,
   507  	) {
   508  		t.Log("did nonTableDescriptorRangeCount() change?")
   509  		t.Logf(
   510  			"claimed numbers:\ntime series = %d\ninternal = %d\nsystemdb = %d (%v)",
   511  			tsCount, internalCount, systemTableRangeCount, sysDBMap,
   512  		)
   513  		db := testCluster.ServerConn(0)
   514  		defer db.Close()
   515  
   516  		runner := sqlutils.MakeSQLRunner(db)
   517  		s := sqlutils.MatrixToStr(runner.QueryStr(t, `
   518  select range_id, database_name, table_name, start_pretty, end_pretty from crdb_internal.ranges order by range_id asc`,
   519  		))
   520  		t.Logf("actual ranges:\n%s", s)
   521  	}
   522  }
   523  
   524  func TestAdminAPITableDoesNotExist(t *testing.T) {
   525  	defer leaktest.AfterTest(t)()
   526  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   527  	defer s.Stopper().Stop(context.Background())
   528  
   529  	const fakename = "i_do_not_exist"
   530  	const badDBPath = "databases/" + fakename + "/tables/foo"
   531  	const dbErrPattern = `relation \\"` + fakename + `.foo\\" does not exist`
   532  	if err := getAdminJSONProto(s, badDBPath, nil); !testutils.IsError(err, dbErrPattern) {
   533  		t.Fatalf("unexpected error: %v\nexpected: %s", err, dbErrPattern)
   534  	}
   535  
   536  	const badTablePath = "databases/system/tables/" + fakename
   537  	const tableErrPattern = `relation \\"system.` + fakename + `\\" does not exist`
   538  	if err := getAdminJSONProto(s, badTablePath, nil); !testutils.IsError(err, tableErrPattern) {
   539  		t.Fatalf("unexpected error: %v\nexpected: %s", err, tableErrPattern)
   540  	}
   541  }
   542  
   543  func TestAdminAPITableSQLInjection(t *testing.T) {
   544  	defer leaktest.AfterTest(t)()
   545  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   546  	defer s.Stopper().Stop(context.Background())
   547  
   548  	const fakeTable = "users;DROP DATABASE system;"
   549  	const path = "databases/system/tables/" + fakeTable
   550  	const errPattern = `relation \"system.` + fakeTable + `\" does not exist`
   551  	if err := getAdminJSONProto(s, path, nil); !testutils.IsError(err, regexp.QuoteMeta(errPattern)) {
   552  		t.Fatalf("unexpected error: %v\nexpected: %s", err, errPattern)
   553  	}
   554  }
   555  
   556  func TestAdminAPITableDetails(t *testing.T) {
   557  	defer leaktest.AfterTest(t)()
   558  
   559  	for _, tc := range []struct {
   560  		name, dbName, tblName string
   561  	}{
   562  		{name: "lower", dbName: "test", tblName: "tbl"},
   563  		{name: "lower with space", dbName: "test test", tblName: "tbl tbl"},
   564  		{name: "upper", dbName: "TEST", tblName: "TBL"}, // Regression test for issue #14056
   565  	} {
   566  		t.Run(tc.name, func(t *testing.T) {
   567  			s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
   568  			defer s.Stopper().Stop(context.Background())
   569  			ts := s.(*TestServer)
   570  
   571  			escDBName := tree.NameStringP(&tc.dbName)
   572  			escTblName := tree.NameStringP(&tc.tblName)
   573  
   574  			ac := log.AmbientContext{Tracer: s.ClusterSettings().Tracer}
   575  			ctx, span := ac.AnnotateCtxWithSpan(context.Background(), "test")
   576  			defer span.Finish()
   577  
   578  			setupQueries := []string{
   579  				fmt.Sprintf("CREATE DATABASE %s", escDBName),
   580  				fmt.Sprintf(`CREATE TABLE %s.%s (
   581  							nulls_allowed INT8,
   582  							nulls_not_allowed INT8 NOT NULL DEFAULT 1000,
   583  							default2 INT8 DEFAULT 2,
   584  							string_default STRING DEFAULT 'default_string',
   585  						  INDEX descidx (default2 DESC)
   586  						)`, escDBName, escTblName),
   587  				fmt.Sprintf("CREATE USER readonly"),
   588  				fmt.Sprintf("CREATE USER app"),
   589  				fmt.Sprintf("GRANT SELECT ON %s.%s TO readonly", escDBName, escTblName),
   590  				fmt.Sprintf("GRANT SELECT,UPDATE,DELETE ON %s.%s TO app", escDBName, escTblName),
   591  			}
   592  
   593  			for _, q := range setupQueries {
   594  				if _, err := db.Exec(q); err != nil {
   595  					t.Fatal(err)
   596  				}
   597  			}
   598  
   599  			// Perform API call.
   600  			var resp serverpb.TableDetailsResponse
   601  			url := fmt.Sprintf("databases/%s/tables/%s", tc.dbName, tc.tblName)
   602  			if err := getAdminJSONProto(s, url, &resp); err != nil {
   603  				t.Fatal(err)
   604  			}
   605  
   606  			// Verify columns.
   607  			expColumns := []serverpb.TableDetailsResponse_Column{
   608  				{Name: "nulls_allowed", Type: "INT8", Nullable: true, DefaultValue: ""},
   609  				{Name: "nulls_not_allowed", Type: "INT8", Nullable: false, DefaultValue: "1000:::INT8"},
   610  				{Name: "default2", Type: "INT8", Nullable: true, DefaultValue: "2:::INT8"},
   611  				{Name: "string_default", Type: "STRING", Nullable: true, DefaultValue: "'default_string':::STRING"},
   612  				{Name: "rowid", Type: "INT8", Nullable: false, DefaultValue: "unique_rowid()", Hidden: true},
   613  			}
   614  			testutils.SortStructs(expColumns, "Name")
   615  			testutils.SortStructs(resp.Columns, "Name")
   616  			if a, e := len(resp.Columns), len(expColumns); a != e {
   617  				t.Fatalf("# of result columns %d != expected %d (got: %#v)", a, e, resp.Columns)
   618  			}
   619  			for i, a := range resp.Columns {
   620  				e := expColumns[i]
   621  				if a.String() != e.String() {
   622  					t.Fatalf("mismatch at column %d: actual %#v != %#v", i, a, e)
   623  				}
   624  			}
   625  
   626  			// Verify grants.
   627  			expGrants := []serverpb.TableDetailsResponse_Grant{
   628  				{User: sqlbase.AdminRole, Privileges: []string{"ALL"}},
   629  				{User: security.RootUser, Privileges: []string{"ALL"}},
   630  				{User: "app", Privileges: []string{"DELETE"}},
   631  				{User: "app", Privileges: []string{"SELECT"}},
   632  				{User: "app", Privileges: []string{"UPDATE"}},
   633  				{User: "readonly", Privileges: []string{"SELECT"}},
   634  			}
   635  			testutils.SortStructs(expGrants, "User")
   636  			testutils.SortStructs(resp.Grants, "User")
   637  			if a, e := len(resp.Grants), len(expGrants); a != e {
   638  				t.Fatalf("# of grant columns %d != expected %d (got: %#v)", a, e, resp.Grants)
   639  			}
   640  			for i, a := range resp.Grants {
   641  				e := expGrants[i]
   642  				sort.Strings(a.Privileges)
   643  				sort.Strings(e.Privileges)
   644  				if a.String() != e.String() {
   645  					t.Fatalf("mismatch at index %d: actual %#v != %#v", i, a, e)
   646  				}
   647  			}
   648  
   649  			// Verify indexes.
   650  			expIndexes := []serverpb.TableDetailsResponse_Index{
   651  				{Name: "primary", Column: "rowid", Direction: "ASC", Unique: true, Seq: 1},
   652  				{Name: "descidx", Column: "rowid", Direction: "ASC", Unique: false, Seq: 2, Implicit: true},
   653  				{Name: "descidx", Column: "default2", Direction: "DESC", Unique: false, Seq: 1},
   654  			}
   655  			testutils.SortStructs(expIndexes, "Name", "Seq")
   656  			testutils.SortStructs(resp.Indexes, "Name", "Seq")
   657  			for i, a := range resp.Indexes {
   658  				e := expIndexes[i]
   659  				if a.String() != e.String() {
   660  					t.Fatalf("mismatch at index %d: actual %#v != %#v", i, a, e)
   661  				}
   662  			}
   663  
   664  			// Verify range count.
   665  			if a, e := resp.RangeCount, int64(1); a != e {
   666  				t.Fatalf("# of ranges %d != expected %d", a, e)
   667  			}
   668  
   669  			// Verify Create Table Statement.
   670  			{
   671  
   672  				showCreateTableQuery := fmt.Sprintf("SHOW CREATE TABLE %s.%s", escDBName, escTblName)
   673  
   674  				row := db.QueryRow(showCreateTableQuery)
   675  				var createStmt, tableName string
   676  				if err := row.Scan(&tableName, &createStmt); err != nil {
   677  					t.Fatal(err)
   678  				}
   679  
   680  				if a, e := resp.CreateTableStatement, createStmt; a != e {
   681  					t.Fatalf("mismatched create table statement; expected %s, got %s", e, a)
   682  				}
   683  			}
   684  
   685  			// Verify Descriptor ID.
   686  			path, err := ts.admin.queryDescriptorIDPath(ctx,
   687  				security.RootUser, []string{tc.dbName, tc.tblName})
   688  			if err != nil {
   689  				t.Fatal(err)
   690  			}
   691  			if a, e := resp.DescriptorID, int64(path[2]); a != e {
   692  				t.Fatalf("table had descriptorID %d, expected %d", a, e)
   693  			}
   694  		})
   695  	}
   696  }
   697  
   698  // TestAdminAPIZoneDetails verifies the zone configuration information returned
   699  // for both DatabaseDetailsResponse AND TableDetailsResponse.
   700  func TestAdminAPIZoneDetails(t *testing.T) {
   701  	defer leaktest.AfterTest(t)()
   702  	s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
   703  	defer s.Stopper().Stop(context.Background())
   704  	ts := s.(*TestServer)
   705  
   706  	// Create database and table.
   707  	ac := log.AmbientContext{Tracer: s.ClusterSettings().Tracer}
   708  	ctx, span := ac.AnnotateCtxWithSpan(context.Background(), "test")
   709  	defer span.Finish()
   710  	setupQueries := []string{
   711  		"CREATE DATABASE test",
   712  		"CREATE TABLE test.tbl (val STRING)",
   713  	}
   714  	for _, q := range setupQueries {
   715  		if _, err := db.Exec(q); err != nil {
   716  			t.Fatalf("error executing '%s': %s", q, err)
   717  		}
   718  	}
   719  
   720  	// Function to verify the zone for table "test.tbl" as returned by the Admin
   721  	// API.
   722  	verifyTblZone := func(
   723  		expectedZone zonepb.ZoneConfig, expectedLevel serverpb.ZoneConfigurationLevel,
   724  	) {
   725  		var resp serverpb.TableDetailsResponse
   726  		if err := getAdminJSONProto(s, "databases/test/tables/tbl", &resp); err != nil {
   727  			t.Fatal(err)
   728  		}
   729  		if a, e := &resp.ZoneConfig, &expectedZone; !proto.Equal(a, e) {
   730  			t.Errorf("actual table zone config %v did not match expected value %v", a, e)
   731  		}
   732  		if a, e := resp.ZoneConfigLevel, expectedLevel; a != e {
   733  			t.Errorf("actual table ZoneConfigurationLevel %s did not match expected value %s", a, e)
   734  		}
   735  		if t.Failed() {
   736  			t.FailNow()
   737  		}
   738  	}
   739  
   740  	// Function to verify the zone for database "test" as returned by the Admin
   741  	// API.
   742  	verifyDbZone := func(
   743  		expectedZone zonepb.ZoneConfig, expectedLevel serverpb.ZoneConfigurationLevel,
   744  	) {
   745  		var resp serverpb.DatabaseDetailsResponse
   746  		if err := getAdminJSONProto(s, "databases/test", &resp); err != nil {
   747  			t.Fatal(err)
   748  		}
   749  		if a, e := &resp.ZoneConfig, &expectedZone; !proto.Equal(a, e) {
   750  			t.Errorf("actual db zone config %v did not match expected value %v", a, e)
   751  		}
   752  		if a, e := resp.ZoneConfigLevel, expectedLevel; a != e {
   753  			t.Errorf("actual db ZoneConfigurationLevel %s did not match expected value %s", a, e)
   754  		}
   755  		if t.Failed() {
   756  			t.FailNow()
   757  		}
   758  	}
   759  
   760  	// Function to store a zone config for a given object ID.
   761  	setZone := func(zoneCfg zonepb.ZoneConfig, id sqlbase.ID) {
   762  		zoneBytes, err := protoutil.Marshal(&zoneCfg)
   763  		if err != nil {
   764  			t.Fatal(err)
   765  		}
   766  		const query = `INSERT INTO system.zones VALUES($1, $2)`
   767  		if _, err := db.Exec(query, id, zoneBytes); err != nil {
   768  			t.Fatalf("error executing '%s': %s", query, err)
   769  		}
   770  	}
   771  
   772  	// Verify zone matches cluster default.
   773  	verifyDbZone(s.(*TestServer).Cfg.DefaultZoneConfig, serverpb.ZoneConfigurationLevel_CLUSTER)
   774  	verifyTblZone(s.(*TestServer).Cfg.DefaultZoneConfig, serverpb.ZoneConfigurationLevel_CLUSTER)
   775  
   776  	// Get ID path for table. This will be an array of three IDs, containing the ID of the root namespace,
   777  	// the database, and the table (in that order).
   778  	idPath, err := ts.admin.queryDescriptorIDPath(ctx, security.RootUser, []string{"test", "tbl"})
   779  	if err != nil {
   780  		t.Fatal(err)
   781  	}
   782  
   783  	// Apply zone configuration to database and check again.
   784  	dbZone := zonepb.ZoneConfig{
   785  		RangeMinBytes: proto.Int64(456),
   786  	}
   787  	setZone(dbZone, idPath[1])
   788  	verifyDbZone(dbZone, serverpb.ZoneConfigurationLevel_DATABASE)
   789  	verifyTblZone(dbZone, serverpb.ZoneConfigurationLevel_DATABASE)
   790  
   791  	// Apply zone configuration to table and check again.
   792  	tblZone := zonepb.ZoneConfig{
   793  		RangeMinBytes: proto.Int64(789),
   794  	}
   795  	setZone(tblZone, idPath[2])
   796  	verifyDbZone(dbZone, serverpb.ZoneConfigurationLevel_DATABASE)
   797  	verifyTblZone(tblZone, serverpb.ZoneConfigurationLevel_TABLE)
   798  }
   799  
   800  func TestAdminAPIUsers(t *testing.T) {
   801  	defer leaktest.AfterTest(t)()
   802  	s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
   803  	defer s.Stopper().Stop(context.Background())
   804  
   805  	// Create sample users.
   806  	query := `
   807  INSERT INTO system.users (username, "hashedPassword")
   808  VALUES ('adminUser', 'abc'), ('bob', 'xyz')`
   809  	if _, err := db.Exec(query); err != nil {
   810  		t.Fatal(err)
   811  	}
   812  
   813  	// Query the API for users.
   814  	var resp serverpb.UsersResponse
   815  	if err := getAdminJSONProto(s, "users", &resp); err != nil {
   816  		t.Fatal(err)
   817  	}
   818  	expResult := serverpb.UsersResponse{
   819  		Users: []serverpb.UsersResponse_User{
   820  			{Username: "adminUser"},
   821  			{Username: "authentic_user"},
   822  			{Username: "bob"},
   823  			{Username: "root"},
   824  		},
   825  	}
   826  
   827  	// Verify results.
   828  	const sortKey = "Username"
   829  	testutils.SortStructs(resp.Users, sortKey)
   830  	testutils.SortStructs(expResult.Users, sortKey)
   831  	if !reflect.DeepEqual(resp, expResult) {
   832  		t.Fatalf("result %v != expected %v", resp, expResult)
   833  	}
   834  }
   835  
   836  func TestAdminAPIEvents(t *testing.T) {
   837  	defer leaktest.AfterTest(t)()
   838  	s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
   839  	defer s.Stopper().Stop(context.Background())
   840  
   841  	setupQueries := []string{
   842  		"CREATE DATABASE api_test",
   843  		"CREATE TABLE api_test.tbl1 (a INT)",
   844  		"CREATE TABLE api_test.tbl2 (a INT)",
   845  		"CREATE TABLE api_test.tbl3 (a INT)",
   846  		"DROP TABLE api_test.tbl1",
   847  		"DROP TABLE api_test.tbl2",
   848  		"SET CLUSTER SETTING cluster.organization = 'somestring';",
   849  	}
   850  	for _, q := range setupQueries {
   851  		if _, err := db.Exec(q); err != nil {
   852  			t.Fatalf("error executing '%s': %s", q, err)
   853  		}
   854  	}
   855  
   856  	const allEvents = ""
   857  	type testcase struct {
   858  		eventType  sql.EventLogType
   859  		hasLimit   bool
   860  		limit      int
   861  		unredacted bool
   862  		expCount   int
   863  	}
   864  	testcases := []testcase{
   865  		{sql.EventLogNodeJoin, false, 0, false, 1},
   866  		{sql.EventLogNodeRestart, false, 0, false, 0},
   867  		{sql.EventLogDropDatabase, false, 0, false, 0},
   868  		{sql.EventLogCreateDatabase, false, 0, false, 3},
   869  		{sql.EventLogDropTable, false, 0, false, 2},
   870  		{sql.EventLogCreateTable, false, 0, false, 3},
   871  		{sql.EventLogSetClusterSetting, false, 0, false, 4},
   872  		// We use limit=true with no limit here because otherwise the
   873  		// expCount will mess up the expected total count below.
   874  		{sql.EventLogSetClusterSetting, true, 0, true, 4},
   875  		{sql.EventLogCreateTable, true, 0, false, 3},
   876  		{sql.EventLogCreateTable, true, -1, false, 3},
   877  		{sql.EventLogCreateTable, true, 2, false, 2},
   878  	}
   879  	minTotalEvents := 0
   880  	for _, tc := range testcases {
   881  		if !tc.hasLimit {
   882  			minTotalEvents += tc.expCount
   883  		}
   884  	}
   885  	testcases = append(testcases, testcase{allEvents, false, 0, false, minTotalEvents})
   886  
   887  	for i, tc := range testcases {
   888  		url := "events"
   889  		if tc.eventType != allEvents {
   890  			url += "?type=" + string(tc.eventType)
   891  			if tc.hasLimit {
   892  				url += fmt.Sprintf("&limit=%d", tc.limit)
   893  			}
   894  			if tc.unredacted {
   895  				url += fmt.Sprintf("&unredacted_events=true")
   896  			}
   897  		}
   898  
   899  		t.Run(url, func(t *testing.T) {
   900  			var resp serverpb.EventsResponse
   901  			if err := getAdminJSONProto(s, url, &resp); err != nil {
   902  				t.Fatal(err)
   903  			}
   904  			if tc.eventType == allEvents {
   905  				// When retrieving all events, we expect that there will be some system
   906  				// database migrations, unrelated to this test, that add to the log entry
   907  				// count. So, we do a looser check here.
   908  				if a, min := len(resp.Events), tc.expCount; a < tc.expCount {
   909  					t.Fatalf("%d: total # of events %d < min %d", i, a, min)
   910  				}
   911  			} else {
   912  				if a, e := len(resp.Events), tc.expCount; a != e {
   913  					t.Fatalf("%d: # of %s events %d != expected %d", i, tc.eventType, a, e)
   914  				}
   915  			}
   916  
   917  			// Ensure we don't have blank / nonsensical fields.
   918  			for _, e := range resp.Events {
   919  				if e.Timestamp == (time.Time{}) {
   920  					t.Errorf("%d: missing/empty timestamp", i)
   921  				}
   922  
   923  				if len(tc.eventType) > 0 {
   924  					if a, e := e.EventType, string(tc.eventType); a != e {
   925  						t.Errorf("%d: event type %s != expected %s", i, a, e)
   926  					}
   927  				} else {
   928  					if len(e.EventType) == 0 {
   929  						t.Errorf("%d: missing event type in event", i)
   930  					}
   931  				}
   932  
   933  				isSettingChange := e.EventType == string(sql.EventLogSetClusterSetting)
   934  
   935  				if e.TargetID == 0 && !isSettingChange {
   936  					t.Errorf("%d: missing/empty TargetID", i)
   937  				}
   938  				if e.ReportingID == 0 {
   939  					t.Errorf("%d: missing/empty ReportingID", i)
   940  				}
   941  				if len(e.Info) == 0 {
   942  					t.Errorf("%d: missing/empty Info", i)
   943  				}
   944  				if isSettingChange && strings.Contains(e.Info, "cluster.organization") {
   945  					if tc.unredacted {
   946  						if !strings.Contains(e.Info, "somestring") {
   947  							t.Errorf("%d: require 'somestring' in Info", i)
   948  						}
   949  					} else {
   950  						if strings.Contains(e.Info, "somestring") {
   951  							t.Errorf("%d: un-redacted 'somestring' in Info", i)
   952  						}
   953  					}
   954  				}
   955  				if len(e.UniqueID) == 0 {
   956  					t.Errorf("%d: missing/empty UniqueID", i)
   957  				}
   958  			}
   959  		})
   960  	}
   961  }
   962  
   963  func TestAdminAPISettings(t *testing.T) {
   964  	defer leaktest.AfterTest(t)()
   965  
   966  	sc := log.Scope(t)
   967  	defer sc.Close(t)
   968  
   969  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
   970  	defer s.Stopper().Stop(context.Background())
   971  
   972  	// Any bool that defaults to true will work here.
   973  	const settingKey = "sql.metrics.statement_details.enabled"
   974  	st := s.ClusterSettings()
   975  	allKeys := settings.Keys()
   976  
   977  	checkSetting := func(t *testing.T, k string, v serverpb.SettingsResponse_Value) {
   978  		ref, ok := settings.Lookup(k, settings.LookupForReporting)
   979  		if !ok {
   980  			t.Fatalf("%s: not found after initial lookup", k)
   981  		}
   982  		typ := ref.Typ()
   983  
   984  		if !settings.TestingIsReportable(ref) {
   985  			if v.Value != "<redacted>" && v.Value != "" {
   986  				t.Errorf("%s: expected redacted value for %v, got %s", k, ref, v.Value)
   987  			}
   988  		} else {
   989  			if ref.String(&st.SV) != v.Value {
   990  				t.Errorf("%s: expected value %v, got %s", k, ref, v.Value)
   991  			}
   992  		}
   993  
   994  		if expectedPublic := ref.Visibility() == settings.Public; expectedPublic != v.Public {
   995  			t.Errorf("%s: expected public %v, got %v", k, expectedPublic, v.Public)
   996  		}
   997  
   998  		if desc := ref.Description(); desc != v.Description {
   999  			t.Errorf("%s: expected description %s, got %s", k, desc, v.Description)
  1000  		}
  1001  		if typ != v.Type {
  1002  			t.Errorf("%s: expected type %s, got %s", k, typ, v.Type)
  1003  		}
  1004  	}
  1005  
  1006  	t.Run("all", func(t *testing.T) {
  1007  		var resp serverpb.SettingsResponse
  1008  
  1009  		if err := getAdminJSONProto(s, "settings", &resp); err != nil {
  1010  			t.Fatal(err)
  1011  		}
  1012  
  1013  		// Check that all expected keys were returned
  1014  		if len(allKeys) != len(resp.KeyValues) {
  1015  			t.Fatalf("expected %d keys, got %d", len(allKeys), len(resp.KeyValues))
  1016  		}
  1017  		for _, k := range allKeys {
  1018  			if _, ok := resp.KeyValues[k]; !ok {
  1019  				t.Fatalf("expected key %s not found in response", k)
  1020  			}
  1021  		}
  1022  
  1023  		// Check that the test key is listed and the values come indeed
  1024  		// from the settings package unchanged.
  1025  		seenRef := false
  1026  		for k, v := range resp.KeyValues {
  1027  			if k == settingKey {
  1028  				seenRef = true
  1029  				if v.Value != "true" {
  1030  					t.Errorf("%s: expected true, got %s", k, v.Value)
  1031  				}
  1032  			}
  1033  
  1034  			checkSetting(t, k, v)
  1035  		}
  1036  
  1037  		if !seenRef {
  1038  			t.Fatalf("failed to observe test setting %s, got %+v", settingKey, resp.KeyValues)
  1039  		}
  1040  	})
  1041  
  1042  	t.Run("one-by-one", func(t *testing.T) {
  1043  		var resp serverpb.SettingsResponse
  1044  
  1045  		// All the settings keys must be retrievable, and their
  1046  		// type and description must match.
  1047  		for _, k := range allKeys {
  1048  			q := make(url.Values)
  1049  			q.Add("keys", k)
  1050  			url := "settings?" + q.Encode()
  1051  			if err := getAdminJSONProto(s, url, &resp); err != nil {
  1052  				t.Fatalf("%s: %v", k, err)
  1053  			}
  1054  			if len(resp.KeyValues) != 1 {
  1055  				t.Fatalf("%s: expected 1 response, got %d", k, len(resp.KeyValues))
  1056  			}
  1057  			v, ok := resp.KeyValues[k]
  1058  			if !ok {
  1059  				t.Fatalf("%s: response does not contain key", k)
  1060  			}
  1061  
  1062  			checkSetting(t, k, v)
  1063  		}
  1064  	})
  1065  }
  1066  
  1067  // TestAdminAPIUIData checks that UI customizations are properly
  1068  // persisted for both admin and non-admin users.
  1069  func TestAdminAPIUIData(t *testing.T) {
  1070  	defer leaktest.AfterTest(t)()
  1071  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1072  	defer s.Stopper().Stop(context.Background())
  1073  
  1074  	testutils.RunTrueAndFalse(t, "isAdmin", func(t *testing.T, isAdmin bool) {
  1075  		start := timeutil.Now()
  1076  
  1077  		mustSetUIData := func(keyValues map[string][]byte) {
  1078  			if err := postAdminJSONProtoWithAdminOption(s, "uidata", &serverpb.SetUIDataRequest{
  1079  				KeyValues: keyValues,
  1080  			}, &serverpb.SetUIDataResponse{}, isAdmin); err != nil {
  1081  				t.Fatal(err)
  1082  			}
  1083  		}
  1084  
  1085  		expectKeyValues := func(expKeyValues map[string][]byte) {
  1086  			var resp serverpb.GetUIDataResponse
  1087  			queryValues := make(url.Values)
  1088  			for key := range expKeyValues {
  1089  				queryValues.Add("keys", key)
  1090  			}
  1091  			url := "uidata?" + queryValues.Encode()
  1092  			if err := getAdminJSONProtoWithAdminOption(s, url, &resp, isAdmin); err != nil {
  1093  				t.Fatal(err)
  1094  			}
  1095  			// Do a two-way comparison. We can't use reflect.DeepEqual(), because
  1096  			// resp.KeyValues has timestamps and expKeyValues doesn't.
  1097  			for key, actualVal := range resp.KeyValues {
  1098  				if a, e := actualVal.Value, expKeyValues[key]; !bytes.Equal(a, e) {
  1099  					t.Fatalf("key %s: value = %v, expected = %v", key, a, e)
  1100  				}
  1101  			}
  1102  			for key, expVal := range expKeyValues {
  1103  				if a, e := resp.KeyValues[key].Value, expVal; !bytes.Equal(a, e) {
  1104  					t.Fatalf("key %s: value = %v, expected = %v", key, a, e)
  1105  				}
  1106  			}
  1107  
  1108  			// Sanity check LastUpdated.
  1109  			for _, val := range resp.KeyValues {
  1110  				now := timeutil.Now()
  1111  				if val.LastUpdated.Before(start) {
  1112  					t.Fatalf("val.LastUpdated %s < start %s", val.LastUpdated, start)
  1113  				}
  1114  				if val.LastUpdated.After(now) {
  1115  					t.Fatalf("val.LastUpdated %s > now %s", val.LastUpdated, now)
  1116  				}
  1117  			}
  1118  		}
  1119  
  1120  		expectValueEquals := func(key string, expVal []byte) {
  1121  			expectKeyValues(map[string][]byte{key: expVal})
  1122  		}
  1123  
  1124  		expectKeyNotFound := func(key string) {
  1125  			var resp serverpb.GetUIDataResponse
  1126  			url := "uidata?keys=" + key
  1127  			if err := getAdminJSONProtoWithAdminOption(s, url, &resp, isAdmin); err != nil {
  1128  				t.Fatal(err)
  1129  			}
  1130  			if len(resp.KeyValues) != 0 {
  1131  				t.Fatal("key unexpectedly found")
  1132  			}
  1133  		}
  1134  
  1135  		// Basic tests.
  1136  		var badResp serverpb.GetUIDataResponse
  1137  		const errPattern = "400 Bad Request"
  1138  		if err := getAdminJSONProtoWithAdminOption(s, "uidata", &badResp, isAdmin); !testutils.IsError(err, errPattern) {
  1139  			t.Fatalf("unexpected error: %v\nexpected: %s", err, errPattern)
  1140  		}
  1141  
  1142  		mustSetUIData(map[string][]byte{"k1": []byte("v1")})
  1143  		expectValueEquals("k1", []byte("v1"))
  1144  
  1145  		expectKeyNotFound("NON_EXISTENT_KEY")
  1146  
  1147  		mustSetUIData(map[string][]byte{
  1148  			"k2": []byte("v2"),
  1149  			"k3": []byte("v3"),
  1150  		})
  1151  		expectValueEquals("k2", []byte("v2"))
  1152  		expectValueEquals("k3", []byte("v3"))
  1153  		expectKeyValues(map[string][]byte{
  1154  			"k2": []byte("v2"),
  1155  			"k3": []byte("v3"),
  1156  		})
  1157  
  1158  		mustSetUIData(map[string][]byte{"k2": []byte("v2-updated")})
  1159  		expectKeyValues(map[string][]byte{
  1160  			"k2": []byte("v2-updated"),
  1161  			"k3": []byte("v3"),
  1162  		})
  1163  
  1164  		// Write a binary blob with all possible byte values, then verify it.
  1165  		var buf bytes.Buffer
  1166  		for i := 0; i < 997; i++ {
  1167  			buf.WriteByte(byte(i % 256))
  1168  		}
  1169  		mustSetUIData(map[string][]byte{"bin": buf.Bytes()})
  1170  		expectValueEquals("bin", buf.Bytes())
  1171  	})
  1172  }
  1173  
  1174  // TestAdminAPIUISeparateData check that separate users have separate customizations.
  1175  func TestAdminAPIUISeparateData(t *testing.T) {
  1176  	defer leaktest.AfterTest(t)()
  1177  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1178  	defer s.Stopper().Stop(context.Background())
  1179  
  1180  	// Make a setting for an admin user.
  1181  	if err := postAdminJSONProtoWithAdminOption(s, "uidata",
  1182  		&serverpb.SetUIDataRequest{KeyValues: map[string][]byte{"k": []byte("v1")}},
  1183  		&serverpb.SetUIDataResponse{},
  1184  		true /*isAdmin*/); err != nil {
  1185  		t.Fatal(err)
  1186  	}
  1187  
  1188  	// Make a setting for a non-admin user.
  1189  	if err := postAdminJSONProtoWithAdminOption(s, "uidata",
  1190  		&serverpb.SetUIDataRequest{KeyValues: map[string][]byte{"k": []byte("v2")}},
  1191  		&serverpb.SetUIDataResponse{},
  1192  		false /*isAdmin*/); err != nil {
  1193  		t.Fatal(err)
  1194  	}
  1195  
  1196  	var resp serverpb.GetUIDataResponse
  1197  	url := "uidata?keys=k"
  1198  
  1199  	if err := getAdminJSONProtoWithAdminOption(s, url, &resp, true /* isAdmin */); err != nil {
  1200  		t.Fatal(err)
  1201  	}
  1202  	if len(resp.KeyValues) != 1 || !bytes.Equal(resp.KeyValues["k"].Value, []byte("v1")) {
  1203  		t.Fatalf("unexpected admin values: %+v", resp.KeyValues)
  1204  	}
  1205  	if err := getAdminJSONProtoWithAdminOption(s, url, &resp, false /* isAdmin */); err != nil {
  1206  		t.Fatal(err)
  1207  	}
  1208  	if len(resp.KeyValues) != 1 || !bytes.Equal(resp.KeyValues["k"].Value, []byte("v2")) {
  1209  		t.Fatalf("unexpected non-admin values: %+v", resp.KeyValues)
  1210  	}
  1211  }
  1212  
  1213  func TestClusterAPI(t *testing.T) {
  1214  	defer leaktest.AfterTest(t)()
  1215  	s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1216  	defer s.Stopper().Stop(context.Background())
  1217  
  1218  	testutils.RunTrueAndFalse(t, "reportingOn", func(t *testing.T, reportingOn bool) {
  1219  		testutils.RunTrueAndFalse(t, "enterpriseOn", func(t *testing.T, enterpriseOn bool) {
  1220  			// Override server license check.
  1221  			if enterpriseOn {
  1222  				old := base.CheckEnterpriseEnabled
  1223  				base.CheckEnterpriseEnabled = func(_ *cluster.Settings, _ uuid.UUID, _, _ string) error {
  1224  					return nil
  1225  				}
  1226  				defer func() { base.CheckEnterpriseEnabled = old }()
  1227  			}
  1228  
  1229  			if _, err := db.Exec(`SET CLUSTER SETTING diagnostics.reporting.enabled = $1`, reportingOn); err != nil {
  1230  				t.Fatal(err)
  1231  			}
  1232  
  1233  			// We need to retry, because the cluster ID isn't set until after
  1234  			// bootstrapping and because setting a cluster setting isn't necessarily
  1235  			// instantaneous.
  1236  			//
  1237  			// Also note that there's a migration that affects `diagnostics.reporting.enabled`,
  1238  			// so manipulating the cluster setting var directly is a bad idea.
  1239  			testutils.SucceedsSoon(t, func() error {
  1240  				var resp serverpb.ClusterResponse
  1241  				if err := getAdminJSONProto(s, "cluster", &resp); err != nil {
  1242  					return err
  1243  				}
  1244  				if a, e := resp.ClusterID, s.RPCContext().ClusterID.String(); a != e {
  1245  					return errors.Errorf("cluster ID %s != expected %s", a, e)
  1246  				}
  1247  				if a, e := resp.ReportingEnabled, reportingOn; a != e {
  1248  					return errors.Errorf("reportingEnabled = %t, wanted %t", a, e)
  1249  				}
  1250  				if a, e := resp.EnterpriseEnabled, enterpriseOn; a != e {
  1251  					return errors.Errorf("enterpriseEnabled = %t, wanted %t", a, e)
  1252  				}
  1253  				return nil
  1254  			})
  1255  		})
  1256  	})
  1257  }
  1258  
  1259  func TestHealthAPI(t *testing.T) {
  1260  	defer leaktest.AfterTest(t)()
  1261  
  1262  	ctx := context.Background()
  1263  
  1264  	s, _, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1265  	defer s.Stopper().Stop(ctx)
  1266  
  1267  	// We need to retry because the node ID isn't set until after
  1268  	// bootstrapping.
  1269  	testutils.SucceedsSoon(t, func() error {
  1270  		var resp serverpb.HealthResponse
  1271  		return getAdminJSONProto(s, "health", &resp)
  1272  	})
  1273  
  1274  	// Expire this node's liveness record by pausing heartbeats and advancing the
  1275  	// server's clock.
  1276  	ts := s.(*TestServer)
  1277  	defer ts.nodeLiveness.DisableAllHeartbeatsForTest()()
  1278  	self, err := ts.nodeLiveness.Self()
  1279  	if err != nil {
  1280  		t.Fatal(err)
  1281  	}
  1282  	s.Clock().Update(hlc.Timestamp(self.Expiration).Add(1, 0))
  1283  
  1284  	var resp serverpb.HealthResponse
  1285  	testutils.SucceedsSoon(t, func() error {
  1286  		err := getAdminJSONProto(s, "health?ready=1", &resp)
  1287  		if err == nil {
  1288  			return errors.New("health OK, still waiting for unhealth")
  1289  		}
  1290  
  1291  		t.Logf("observed error: %v", err)
  1292  		if !testutils.IsError(err, `(?s)503 Service Unavailable.*"error": "node is not healthy"`) {
  1293  			return err
  1294  		}
  1295  		return nil
  1296  	})
  1297  
  1298  	// After the node reports an error with `?ready=1`, the health
  1299  	// endpoint must still succeed without error when `?ready=1` is not specified.
  1300  	if err := getAdminJSONProto(s, "health", &resp); err != nil {
  1301  		t.Fatal(err)
  1302  	}
  1303  }
  1304  
  1305  // getSystemJobIDs queries the jobs table for all jobs IDs. Sorted by decreasing creation time.
  1306  func getSystemJobIDs(t testing.TB, db *sqlutils.SQLRunner) []int64 {
  1307  	rows := db.Query(t, `SELECT job_id FROM crdb_internal.jobs ORDER BY created DESC;`)
  1308  	defer rows.Close()
  1309  
  1310  	res := []int64{}
  1311  	for rows.Next() {
  1312  		var id int64
  1313  		if err := rows.Scan(&id); err != nil {
  1314  			t.Fatal(err)
  1315  		}
  1316  		res = append(res, id)
  1317  	}
  1318  	return res
  1319  }
  1320  
  1321  func TestAdminAPIJobs(t *testing.T) {
  1322  	defer leaktest.AfterTest(t)()
  1323  
  1324  	s, conn, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1325  	defer s.Stopper().Stop(context.Background())
  1326  	sqlDB := sqlutils.MakeSQLRunner(conn)
  1327  
  1328  	// Get list of existing jobs (migrations). Assumed to all have succeeded.
  1329  	existingIDs := getSystemJobIDs(t, sqlDB)
  1330  
  1331  	testJobs := []struct {
  1332  		id       int64
  1333  		status   jobs.Status
  1334  		details  jobspb.Details
  1335  		progress jobspb.ProgressDetails
  1336  		username string
  1337  	}{
  1338  		{1, jobs.StatusRunning, jobspb.RestoreDetails{}, jobspb.RestoreProgress{}, security.RootUser},
  1339  		{2, jobs.StatusRunning, jobspb.BackupDetails{}, jobspb.BackupProgress{}, security.RootUser},
  1340  		{3, jobs.StatusSucceeded, jobspb.BackupDetails{}, jobspb.BackupProgress{}, security.RootUser},
  1341  		{4, jobs.StatusRunning, jobspb.ChangefeedDetails{}, jobspb.ChangefeedProgress{}, security.RootUser},
  1342  		{5, jobs.StatusSucceeded, jobspb.BackupDetails{}, jobspb.BackupProgress{}, authenticatedUserNameNoAdmin},
  1343  	}
  1344  	for _, job := range testJobs {
  1345  		payload := jobspb.Payload{Username: job.username, Details: jobspb.WrapPayloadDetails(job.details)}
  1346  		payloadBytes, err := protoutil.Marshal(&payload)
  1347  		if err != nil {
  1348  			t.Fatal(err)
  1349  		}
  1350  
  1351  		progress := jobspb.Progress{Details: jobspb.WrapProgressDetails(job.progress)}
  1352  		// Populate progress.Progress field with a specific progress type based on
  1353  		// the job type.
  1354  		if _, ok := job.progress.(jobspb.ChangefeedProgress); ok {
  1355  			progress.Progress = &jobspb.Progress_HighWater{
  1356  				HighWater: &hlc.Timestamp{},
  1357  			}
  1358  		} else {
  1359  			progress.Progress = &jobspb.Progress_FractionCompleted{
  1360  				FractionCompleted: 1.0,
  1361  			}
  1362  		}
  1363  
  1364  		progressBytes, err := protoutil.Marshal(&progress)
  1365  		if err != nil {
  1366  			t.Fatal(err)
  1367  		}
  1368  		sqlDB.Exec(t,
  1369  			`INSERT INTO system.jobs (id, status, payload, progress) VALUES ($1, $2, $3, $4)`,
  1370  			job.id, job.status, payloadBytes, progressBytes,
  1371  		)
  1372  	}
  1373  
  1374  	const invalidJobType = math.MaxInt32
  1375  
  1376  	testCases := []struct {
  1377  		uri                    string
  1378  		expectedIDsViaAdmin    []int64
  1379  		expectedIDsViaNonAdmin []int64
  1380  	}{
  1381  		{"jobs", append([]int64{5, 4, 3, 2, 1}, existingIDs...), []int64{5}},
  1382  		{"jobs?limit=1", []int64{5}, []int64{5}},
  1383  		{"jobs?status=running", []int64{4, 2, 1}, []int64{}},
  1384  		{"jobs?status=succeeded", append([]int64{5, 3}, existingIDs...), []int64{5}},
  1385  		{"jobs?status=pending", []int64{}, []int64{}},
  1386  		{"jobs?status=garbage", []int64{}, []int64{}},
  1387  		{fmt.Sprintf("jobs?type=%d", jobspb.TypeBackup), []int64{5, 3, 2}, []int64{5}},
  1388  		{fmt.Sprintf("jobs?type=%d", jobspb.TypeRestore), []int64{1}, []int64{}},
  1389  		{fmt.Sprintf("jobs?type=%d", invalidJobType), []int64{}, []int64{}},
  1390  		{fmt.Sprintf("jobs?status=running&type=%d", jobspb.TypeBackup), []int64{2}, []int64{}},
  1391  	}
  1392  
  1393  	testutils.RunTrueAndFalse(t, "isAdmin", func(t *testing.T, isAdmin bool) {
  1394  		for i, testCase := range testCases {
  1395  			var res serverpb.JobsResponse
  1396  			if err := getAdminJSONProtoWithAdminOption(s, testCase.uri, &res, isAdmin); err != nil {
  1397  				t.Fatal(err)
  1398  			}
  1399  			resIDs := []int64{}
  1400  			for _, job := range res.Jobs {
  1401  				resIDs = append(resIDs, job.ID)
  1402  			}
  1403  
  1404  			expected := testCase.expectedIDsViaAdmin
  1405  			if !isAdmin {
  1406  				expected = testCase.expectedIDsViaNonAdmin
  1407  			}
  1408  
  1409  			if e, a := expected, resIDs; !reflect.DeepEqual(e, a) {
  1410  				t.Errorf("%d: expected job IDs %v, but got %v", i, e, a)
  1411  			}
  1412  		}
  1413  	})
  1414  }
  1415  
  1416  func TestAdminAPILocations(t *testing.T) {
  1417  	defer leaktest.AfterTest(t)()
  1418  
  1419  	s, conn, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1420  	defer s.Stopper().Stop(context.Background())
  1421  	sqlDB := sqlutils.MakeSQLRunner(conn)
  1422  
  1423  	testLocations := []struct {
  1424  		localityKey   string
  1425  		localityValue string
  1426  		latitude      float64
  1427  		longitude     float64
  1428  	}{
  1429  		{"city", "Des Moines", 41.60054, -93.60911},
  1430  		{"city", "New York City", 40.71427, -74.00597},
  1431  		{"city", "Seattle", 47.60621, -122.33207},
  1432  	}
  1433  	for _, loc := range testLocations {
  1434  		sqlDB.Exec(t,
  1435  			`INSERT INTO system.locations ("localityKey", "localityValue", latitude, longitude) VALUES ($1, $2, $3, $4)`,
  1436  			loc.localityKey, loc.localityValue, loc.latitude, loc.longitude,
  1437  		)
  1438  	}
  1439  	var res serverpb.LocationsResponse
  1440  	if err := getAdminJSONProto(s, "locations", &res); err != nil {
  1441  		t.Fatal(err)
  1442  	}
  1443  	for i, loc := range testLocations {
  1444  		expLoc := serverpb.LocationsResponse_Location{
  1445  			LocalityKey:   loc.localityKey,
  1446  			LocalityValue: loc.localityValue,
  1447  			Latitude:      loc.latitude,
  1448  			Longitude:     loc.longitude,
  1449  		}
  1450  		if !reflect.DeepEqual(res.Locations[i], expLoc) {
  1451  			t.Errorf("%d: expected location %v, but got %v", i, expLoc, res.Locations[i])
  1452  		}
  1453  	}
  1454  }
  1455  
  1456  func TestAdminAPIQueryPlan(t *testing.T) {
  1457  	defer leaktest.AfterTest(t)()
  1458  
  1459  	s, conn, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1460  	defer s.Stopper().Stop(context.Background())
  1461  	sqlDB := sqlutils.MakeSQLRunner(conn)
  1462  
  1463  	sqlDB.Exec(t, `CREATE DATABASE api_test`)
  1464  	sqlDB.Exec(t, `CREATE TABLE api_test.t1 (id int primary key, name string)`)
  1465  	sqlDB.Exec(t, `CREATE TABLE api_test.t2 (id int primary key, name string)`)
  1466  
  1467  	testCases := []struct {
  1468  		query string
  1469  		exp   []string
  1470  	}{
  1471  		{"SELECT sum(id) FROM api_test.t1", []string{"nodeNames\":[\"1\"]", "Out: @1"}},
  1472  		{"SELECT sum(1) FROM api_test.t1 JOIN api_test.t2 on t1.id = t2.id", []string{"nodeNames\":[\"1\"]", "Out: @1"}},
  1473  	}
  1474  	for i, testCase := range testCases {
  1475  		var res serverpb.QueryPlanResponse
  1476  		queryParam := url.QueryEscape(testCase.query)
  1477  		if err := getAdminJSONProto(s, fmt.Sprintf("queryplan?query=%s", queryParam), &res); err != nil {
  1478  			t.Errorf("%d: got error %s", i, err)
  1479  		}
  1480  
  1481  		for _, exp := range testCase.exp {
  1482  			if !strings.Contains(res.DistSQLPhysicalQueryPlan, exp) {
  1483  				t.Errorf("%d: expected response %v to contain %s", i, res, exp)
  1484  			}
  1485  		}
  1486  	}
  1487  
  1488  }
  1489  
  1490  func TestAdminAPIRangeLogByRangeID(t *testing.T) {
  1491  	defer leaktest.AfterTest(t)()
  1492  	s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
  1493  	defer s.Stopper().Stop(context.Background())
  1494  
  1495  	rangeID := 654321
  1496  	testCases := []struct {
  1497  		rangeID  int
  1498  		hasLimit bool
  1499  		limit    int
  1500  		expected int
  1501  	}{
  1502  		{rangeID, true, 0, 2},
  1503  		{rangeID, true, -1, 2},
  1504  		{rangeID, true, 1, 1},
  1505  		{rangeID, false, 0, 2},
  1506  		// We'll create one event that has rangeID+1 as the otherRangeID.
  1507  		{rangeID + 1, false, 0, 1},
  1508  	}
  1509  
  1510  	for _, otherRangeID := range []int{rangeID + 1, rangeID + 2} {
  1511  		if _, err := db.Exec(
  1512  			`INSERT INTO system.rangelog (
  1513               timestamp, "rangeID", "otherRangeID", "storeID", "eventType"
  1514             ) VALUES (
  1515               now(), $1, $2, $3, $4
  1516            )`,
  1517  			rangeID, otherRangeID,
  1518  			1, // storeID
  1519  			kvserverpb.RangeLogEventType_add.String(),
  1520  		); err != nil {
  1521  			t.Fatal(err)
  1522  		}
  1523  	}
  1524  
  1525  	for _, tc := range testCases {
  1526  		url := fmt.Sprintf("rangelog/%d", tc.rangeID)
  1527  		if tc.hasLimit {
  1528  			url += fmt.Sprintf("?limit=%d", tc.limit)
  1529  		}
  1530  		t.Run(url, func(t *testing.T) {
  1531  			var resp serverpb.RangeLogResponse
  1532  			if err := getAdminJSONProto(s, url, &resp); err != nil {
  1533  				t.Fatal(err)
  1534  			}
  1535  
  1536  			if e, a := tc.expected, len(resp.Events); e != a {
  1537  				t.Fatalf("expected %d events, got %d", e, a)
  1538  			}
  1539  
  1540  			for _, event := range resp.Events {
  1541  				expID := roachpb.RangeID(tc.rangeID)
  1542  				if event.Event.RangeID != expID && event.Event.OtherRangeID != expID {
  1543  					t.Errorf("expected rangeID or otherRangeID to be %d, got %d and r%d",
  1544  						expID, event.Event.RangeID, event.Event.OtherRangeID)
  1545  				}
  1546  			}
  1547  		})
  1548  	}
  1549  }
  1550  
  1551  // Test the range log API when queries are not filtered by a range ID (like in
  1552  // TestAdminAPIRangeLogByRangeID).
  1553  func TestAdminAPIFullRangeLog(t *testing.T) {
  1554  	defer leaktest.AfterTest(t)()
  1555  	s, db, _ := serverutils.StartServer(t,
  1556  		base.TestServerArgs{
  1557  			Knobs: base.TestingKnobs{
  1558  				Store: &kvserver.StoreTestingKnobs{
  1559  					DisableSplitQueue: true,
  1560  				},
  1561  			},
  1562  		})
  1563  	defer s.Stopper().Stop(context.Background())
  1564  
  1565  	// Insert something in the rangelog table, otherwise it's empty for new
  1566  	// clusters.
  1567  	rows, err := db.Query(`SELECT count(1) FROM system.rangelog`)
  1568  	if err != nil {
  1569  		t.Fatal(err)
  1570  	}
  1571  	if !rows.Next() {
  1572  		t.Fatal("missing row")
  1573  	}
  1574  	var cnt int
  1575  	if err := rows.Scan(&cnt); err != nil {
  1576  		t.Fatal(err)
  1577  	}
  1578  	if err := rows.Close(); err != nil {
  1579  		t.Fatal(err)
  1580  	}
  1581  	if cnt != 0 {
  1582  		t.Fatalf("expected 0 rows in system.rangelog, found: %d", cnt)
  1583  	}
  1584  	const rangeID = 100
  1585  	for i := 0; i < 10; i++ {
  1586  		if _, err := db.Exec(
  1587  			`INSERT INTO system.rangelog (
  1588               timestamp, "rangeID", "storeID", "eventType"
  1589             ) VALUES (now(), $1, 1, $2)`,
  1590  			rangeID,
  1591  			kvserverpb.RangeLogEventType_add.String(),
  1592  		); err != nil {
  1593  			t.Fatal(err)
  1594  		}
  1595  	}
  1596  	expectedEvents := 10
  1597  
  1598  	testCases := []struct {
  1599  		hasLimit bool
  1600  		limit    int
  1601  		expected int
  1602  	}{
  1603  		{false, 0, expectedEvents},
  1604  		{true, 0, expectedEvents},
  1605  		{true, -1, expectedEvents},
  1606  		{true, 1, 1},
  1607  	}
  1608  
  1609  	for _, tc := range testCases {
  1610  		url := "rangelog"
  1611  		if tc.hasLimit {
  1612  			url += fmt.Sprintf("?limit=%d", tc.limit)
  1613  		}
  1614  		t.Run(url, func(t *testing.T) {
  1615  			var resp serverpb.RangeLogResponse
  1616  			if err := getAdminJSONProto(s, url, &resp); err != nil {
  1617  				t.Fatal(err)
  1618  			}
  1619  			events := resp.Events
  1620  			if e, a := tc.expected, len(events); e != a {
  1621  				var sb strings.Builder
  1622  				for _, ev := range events {
  1623  					sb.WriteString(ev.String() + "\n")
  1624  				}
  1625  				t.Fatalf("expected %d events, got %d:\n%s", e, a, sb.String())
  1626  			}
  1627  		})
  1628  	}
  1629  }
  1630  
  1631  func TestAdminAPIDataDistribution(t *testing.T) {
  1632  	defer leaktest.AfterTest(t)()
  1633  
  1634  	testCluster := serverutils.StartTestCluster(t, 3, base.TestClusterArgs{})
  1635  	defer testCluster.Stopper().Stop(context.Background())
  1636  
  1637  	firstServer := testCluster.Server(0)
  1638  	sqlDB := sqlutils.MakeSQLRunner(testCluster.ServerConn(0))
  1639  
  1640  	// Create some tables.
  1641  	sqlDB.Exec(t, `CREATE DATABASE roachblog`)
  1642  	sqlDB.Exec(t, `CREATE TABLE roachblog.posts (id INT PRIMARY KEY, title text, body text)`)
  1643  	sqlDB.Exec(t, `CREATE TABLE roachblog.comments (
  1644  		id INT PRIMARY KEY,
  1645  		post_id INT REFERENCES roachblog.posts,
  1646  		body text
  1647  	)`)
  1648  	// Test special characters in DB and table names.
  1649  	sqlDB.Exec(t, `CREATE DATABASE "sp'ec\ch""ars"`)
  1650  	sqlDB.Exec(t, `CREATE TABLE "sp'ec\ch""ars"."more\spec'chars" (id INT PRIMARY KEY)`)
  1651  
  1652  	// Verify that we see their replicas in the DataDistribution response, evenly spread
  1653  	// across the test cluster's three nodes.
  1654  
  1655  	expectedDatabaseInfo := map[string]serverpb.DataDistributionResponse_DatabaseInfo{
  1656  		"roachblog": {
  1657  			TableInfo: map[string]serverpb.DataDistributionResponse_TableInfo{
  1658  				"posts": {
  1659  					ReplicaCountByNodeId: map[roachpb.NodeID]int64{
  1660  						1: 1,
  1661  						2: 1,
  1662  						3: 1,
  1663  					},
  1664  				},
  1665  				"comments": {
  1666  					ReplicaCountByNodeId: map[roachpb.NodeID]int64{
  1667  						1: 1,
  1668  						2: 1,
  1669  						3: 1,
  1670  					},
  1671  				},
  1672  			},
  1673  		},
  1674  		`sp'ec\ch"ars`: {
  1675  			TableInfo: map[string]serverpb.DataDistributionResponse_TableInfo{
  1676  				`more\spec'chars`: {
  1677  					ReplicaCountByNodeId: map[roachpb.NodeID]int64{
  1678  						1: 1,
  1679  						2: 1,
  1680  						3: 1,
  1681  					},
  1682  				},
  1683  			},
  1684  		},
  1685  	}
  1686  
  1687  	// Wait for the new tables' ranges to be created and replicated.
  1688  	testutils.SucceedsSoon(t, func() error {
  1689  		var resp serverpb.DataDistributionResponse
  1690  		if err := getAdminJSONProto(firstServer, "data_distribution", &resp); err != nil {
  1691  			t.Fatal(err)
  1692  		}
  1693  
  1694  		delete(resp.DatabaseInfo, "system") // delete results for system database.
  1695  		if !reflect.DeepEqual(resp.DatabaseInfo, expectedDatabaseInfo) {
  1696  			return fmt.Errorf("expected %v; got %v", expectedDatabaseInfo, resp.DatabaseInfo)
  1697  		}
  1698  
  1699  		// Don't test anything about the zone configs for now; just verify that something is there.
  1700  		if len(resp.ZoneConfigs) == 0 {
  1701  			return fmt.Errorf("no zone configs returned")
  1702  		}
  1703  
  1704  		return nil
  1705  	})
  1706  
  1707  	// Verify that the request still works after a table has been dropped,
  1708  	// and that dropped_at is set on the dropped table.
  1709  	sqlDB.Exec(t, `DROP TABLE roachblog.comments`)
  1710  
  1711  	var resp serverpb.DataDistributionResponse
  1712  	if err := getAdminJSONProto(firstServer, "data_distribution", &resp); err != nil {
  1713  		t.Fatal(err)
  1714  	}
  1715  
  1716  	if resp.DatabaseInfo["roachblog"].TableInfo["comments"].DroppedAt == nil {
  1717  		t.Fatal("expected roachblog.comments to have dropped_at set but it's nil")
  1718  	}
  1719  
  1720  	// Verify that the request still works after a database has been dropped.
  1721  	sqlDB.Exec(t, `DROP DATABASE roachblog CASCADE`)
  1722  
  1723  	if err := getAdminJSONProto(firstServer, "data_distribution", &resp); err != nil {
  1724  		t.Fatal(err)
  1725  	}
  1726  }
  1727  
  1728  func BenchmarkAdminAPIDataDistribution(b *testing.B) {
  1729  	if testing.Short() {
  1730  		b.Skip("TODO: fix benchmark")
  1731  	}
  1732  	testCluster := serverutils.StartTestCluster(b, 3, base.TestClusterArgs{})
  1733  	defer testCluster.Stopper().Stop(context.Background())
  1734  
  1735  	firstServer := testCluster.Server(0)
  1736  	sqlDB := sqlutils.MakeSQLRunner(testCluster.ServerConn(0))
  1737  
  1738  	sqlDB.Exec(b, `CREATE DATABASE roachblog`)
  1739  
  1740  	// Create a bunch of tables.
  1741  	for i := 0; i < 200; i++ {
  1742  		sqlDB.Exec(
  1743  			b,
  1744  			fmt.Sprintf(`CREATE TABLE roachblog.t%d (id INT PRIMARY KEY, title text, body text)`, i),
  1745  		)
  1746  		// TODO(vilterp): split to increase the number of ranges for each table
  1747  	}
  1748  
  1749  	b.ResetTimer()
  1750  	for n := 0; n < b.N; n++ {
  1751  		var resp serverpb.DataDistributionResponse
  1752  		if err := getAdminJSONProto(firstServer, "data_distribution", &resp); err != nil {
  1753  			b.Fatal(err)
  1754  		}
  1755  	}
  1756  	b.StopTimer()
  1757  }
  1758  
  1759  func TestEnqueueRange(t *testing.T) {
  1760  	defer leaktest.AfterTest(t)()
  1761  	testCluster := serverutils.StartTestCluster(t, 3, base.TestClusterArgs{
  1762  		ReplicationMode: base.ReplicationManual,
  1763  	})
  1764  	defer testCluster.Stopper().Stop(context.Background())
  1765  
  1766  	// Up-replicate r1 to all 3 nodes. We use manual replication to avoid lease
  1767  	// transfers causing temporary conditions in which no store is the
  1768  	// leaseholder, which can break the the tests below.
  1769  	_, err := testCluster.AddReplicas(roachpb.KeyMin, testCluster.Target(1), testCluster.Target(2))
  1770  	if err != nil {
  1771  		t.Fatal(err)
  1772  	}
  1773  
  1774  	// RangeID being queued
  1775  	const realRangeID = 1
  1776  	const fakeRangeID = 999
  1777  
  1778  	// Who we expect responses from.
  1779  	const none = 0
  1780  	const leaseholder = 1
  1781  	const allReplicas = 3
  1782  
  1783  	testCases := []struct {
  1784  		nodeID            roachpb.NodeID
  1785  		queue             string
  1786  		rangeID           roachpb.RangeID
  1787  		expectedDetails   int
  1788  		expectedNonErrors int
  1789  	}{
  1790  		// Success cases
  1791  		{0, "gc", realRangeID, allReplicas, leaseholder},
  1792  		{0, "split", realRangeID, allReplicas, leaseholder},
  1793  		{0, "replicaGC", realRangeID, allReplicas, allReplicas},
  1794  		{0, "RaFtLoG", realRangeID, allReplicas, allReplicas},
  1795  		{0, "RAFTSNAPSHOT", realRangeID, allReplicas, allReplicas},
  1796  		{0, "consistencyChecker", realRangeID, allReplicas, leaseholder},
  1797  		{0, "TIMESERIESmaintenance", realRangeID, allReplicas, leaseholder},
  1798  		{1, "raftlog", realRangeID, leaseholder, leaseholder},
  1799  		{2, "raftlog", realRangeID, leaseholder, 1},
  1800  		{3, "raftlog", realRangeID, leaseholder, 1},
  1801  		// Error cases
  1802  		{0, "gv", realRangeID, allReplicas, none},
  1803  		{0, "GC", fakeRangeID, allReplicas, none},
  1804  	}
  1805  
  1806  	for _, tc := range testCases {
  1807  		t.Run(tc.queue, func(t *testing.T) {
  1808  			req := &serverpb.EnqueueRangeRequest{
  1809  				NodeID:  tc.nodeID,
  1810  				Queue:   tc.queue,
  1811  				RangeID: tc.rangeID,
  1812  			}
  1813  			var resp serverpb.EnqueueRangeResponse
  1814  			if err := postAdminJSONProto(testCluster.Server(0), "enqueue_range", req, &resp); err != nil {
  1815  				t.Fatal(err)
  1816  			}
  1817  			if e, a := tc.expectedDetails, len(resp.Details); e != a {
  1818  				t.Errorf("expected %d details; got %d: %+v", e, a, resp)
  1819  			}
  1820  			var numNonErrors int
  1821  			for _, details := range resp.Details {
  1822  				if len(details.Events) > 0 && details.Error == "" {
  1823  					numNonErrors++
  1824  				}
  1825  			}
  1826  			if tc.expectedNonErrors != numNonErrors {
  1827  				t.Errorf("expected %d non-error details; got %d: %+v", tc.expectedNonErrors, numNonErrors, resp)
  1828  			}
  1829  		})
  1830  	}
  1831  
  1832  	// Finally, test a few more basic error cases.
  1833  	reqs := []*serverpb.EnqueueRangeRequest{
  1834  		{NodeID: -1, Queue: "gc"},
  1835  		{Queue: ""},
  1836  		{RangeID: -1, Queue: "gc"},
  1837  	}
  1838  	for _, req := range reqs {
  1839  		t.Run(fmt.Sprint(req), func(t *testing.T) {
  1840  			var resp serverpb.EnqueueRangeResponse
  1841  			err := postAdminJSONProto(testCluster.Server(0), "enqueue_range", req, &resp)
  1842  			if err == nil {
  1843  				t.Fatalf("unexpected success: %+v", resp)
  1844  			}
  1845  			if !testutils.IsError(err, "400 Bad Request") {
  1846  				t.Fatalf("unexpected error type: %+v", err)
  1847  			}
  1848  		})
  1849  	}
  1850  }
  1851  
  1852  func TestStatsforSpanOnLocalMax(t *testing.T) {
  1853  	defer leaktest.AfterTest(t)()
  1854  	testCluster := serverutils.StartTestCluster(t, 3, base.TestClusterArgs{})
  1855  	defer testCluster.Stopper().Stop(context.Background())
  1856  	firstServer := testCluster.Server(0)
  1857  	adminServer := firstServer.(*TestServer).Server.admin
  1858  
  1859  	underTest := roachpb.Span{
  1860  		Key:    keys.LocalMax,
  1861  		EndKey: keys.SystemPrefix,
  1862  	}
  1863  
  1864  	_, err := adminServer.statsForSpan(context.Background(), underTest)
  1865  	if err != nil {
  1866  		t.Fatal(err)
  1867  	}
  1868  }