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

     1  // Copyright 2017 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_test
    12  
    13  import (
    14  	"context"
    15  	gosql "database/sql"
    16  	"fmt"
    17  	"net/url"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/base"
    22  	"github.com/cockroachdb/cockroach/pkg/settings"
    23  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    24  	"github.com/cockroachdb/cockroach/pkg/testutils"
    25  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    26  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    27  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    28  	"github.com/cockroachdb/errors"
    29  )
    30  
    31  const strKey = "testing.str"
    32  const intKey = "testing.int"
    33  const durationKey = "testing.duration"
    34  const byteSizeKey = "testing.bytesize"
    35  const enumKey = "testing.enum"
    36  
    37  var strA = settings.RegisterValidatedStringSetting(strKey, "desc", "<default>", func(sv *settings.Values, v string) error {
    38  	if len(v) > 15 {
    39  		return errors.Errorf("can't set %s to string longer than 15: %s", strKey, v)
    40  	}
    41  	return nil
    42  })
    43  var intA = settings.RegisterValidatedIntSetting(intKey, "desc", 1, func(v int64) error {
    44  	if v < 0 {
    45  		return errors.Errorf("can't set %s to a negative value: %d", intKey, v)
    46  	}
    47  	return nil
    48  
    49  })
    50  var durationA = settings.RegisterValidatedDurationSetting(durationKey, "desc", time.Minute, func(v time.Duration) error {
    51  	if v < 0 {
    52  		return errors.Errorf("can't set %s to a negative duration: %s", durationKey, v)
    53  	}
    54  	return nil
    55  })
    56  var byteSizeA = settings.RegisterValidatedByteSizeSetting(byteSizeKey, "desc", 1024*1024, func(v int64) error {
    57  	if v < 0 {
    58  		return errors.Errorf("can't set %s to a negative value: %d", byteSizeKey, v)
    59  	}
    60  	return nil
    61  })
    62  var enumA = settings.RegisterEnumSetting(enumKey, "desc", "foo", map[int64]string{1: "foo", 2: "bar"})
    63  
    64  func TestSettingsRefresh(t *testing.T) {
    65  	defer leaktest.AfterTest(t)()
    66  
    67  	// Set up some additional cluster settings to play around with. Note that we
    68  	// need to do this before starting the server, or there will be data races.
    69  	st := cluster.MakeTestingClusterSettings()
    70  	s, rawDB, _ := serverutils.StartServer(t, base.TestServerArgs{Settings: st})
    71  	defer s.Stopper().Stop(context.Background())
    72  
    73  	db := sqlutils.MakeSQLRunner(rawDB)
    74  
    75  	insertQ := `UPSERT INTO system.settings (name, value, "lastUpdated", "valueType")
    76  		VALUES ($1, $2, now(), $3)`
    77  	deleteQ := "DELETE FROM system.settings WHERE name = $1"
    78  
    79  	if expected, actual := "<default>", strA.Get(&st.SV); expected != actual {
    80  		t.Fatalf("expected %v, got %v", expected, actual)
    81  	}
    82  	if expected, actual := int64(1), intA.Get(&st.SV); expected != actual {
    83  		t.Fatalf("expected %v, got %v", expected, actual)
    84  	}
    85  
    86  	// Inserting a new setting is reflected in cache.
    87  	db.Exec(t, insertQ, strKey, "foo", "s")
    88  	db.Exec(t, insertQ, intKey, settings.EncodeInt(2), "i")
    89  	// Wait until we observe the gossip-driven update propagating to cache.
    90  	testutils.SucceedsSoon(t, func() error {
    91  		if expected, actual := "foo", strA.Get(&st.SV); expected != actual {
    92  			return errors.Errorf("expected %v, got %v", expected, actual)
    93  		}
    94  		if expected, actual := int64(2), intA.Get(&st.SV); expected != actual {
    95  			return errors.Errorf("expected %v, got %v", expected, actual)
    96  		}
    97  		return nil
    98  	})
    99  
   100  	// Setting to empty also works.
   101  	db.Exec(t, insertQ, strKey, "", "s")
   102  	testutils.SucceedsSoon(t, func() error {
   103  		if expected, actual := "", strA.Get(&st.SV); expected != actual {
   104  			return errors.Errorf("expected %v, got %v", expected, actual)
   105  		}
   106  		return nil
   107  	})
   108  
   109  	// An unknown value doesn't block updates to a known one.
   110  	db.Exec(t, insertQ, "dne", "???", "s")
   111  	db.Exec(t, insertQ, strKey, "qux", "s")
   112  
   113  	testutils.SucceedsSoon(t, func() error {
   114  		if expected, actual := "qux", strA.Get(&st.SV); expected != actual {
   115  			return errors.Errorf("expected %v, got %v", expected, actual)
   116  		}
   117  		if expected, actual := int64(2), intA.Get(&st.SV); expected != actual {
   118  			return errors.Errorf("expected %v, got %v", expected, actual)
   119  		}
   120  		return nil
   121  	})
   122  
   123  	// A malformed value doesn't revert previous set or block other changes.
   124  	db.Exec(t, deleteQ, "dne")
   125  	db.Exec(t, insertQ, intKey, "invalid", "i")
   126  	db.Exec(t, insertQ, strKey, "after-invalid", "s")
   127  
   128  	testutils.SucceedsSoon(t, func() error {
   129  		if expected, actual := int64(2), intA.Get(&st.SV); expected != actual {
   130  			return errors.Errorf("expected %v, got %v", expected, actual)
   131  		}
   132  		if expected, actual := "after-invalid", strA.Get(&st.SV); expected != actual {
   133  			return errors.Errorf("expected %v, got %v", expected, actual)
   134  		}
   135  		return nil
   136  	})
   137  
   138  	// A mis-typed value doesn't revert a previous set or block other changes.
   139  	db.Exec(t, insertQ, intKey, settings.EncodeInt(7), "b")
   140  	db.Exec(t, insertQ, strKey, "after-mistype", "s")
   141  
   142  	testutils.SucceedsSoon(t, func() error {
   143  		if expected, actual := int64(2), intA.Get(&st.SV); expected != actual {
   144  			return errors.Errorf("expected %v, got %v", expected, actual)
   145  		}
   146  		if expected, actual := "after-mistype", strA.Get(&st.SV); expected != actual {
   147  			return errors.Errorf("expected %v, got %v", expected, actual)
   148  		}
   149  		return nil
   150  	})
   151  
   152  	// An invalid value doesn't revert a previous set or block other changes.
   153  	prevStrA := strA.Get(&st.SV)
   154  	prevIntA := intA.Get(&st.SV)
   155  	prevDurationA := durationA.Get(&st.SV)
   156  	prevByteSizeA := byteSizeA.Get(&st.SV)
   157  	db.Exec(t, insertQ, strKey, "this is too big for this setting", "s")
   158  	db.Exec(t, insertQ, intKey, settings.EncodeInt(-1), "i")
   159  	db.Exec(t, insertQ, durationKey, settings.EncodeDuration(-time.Minute), "d")
   160  	db.Exec(t, insertQ, byteSizeKey, settings.EncodeInt(-1), "z")
   161  
   162  	testutils.SucceedsSoon(t, func() error {
   163  		if expected, actual := prevStrA, strA.Get(&st.SV); expected != actual {
   164  			return errors.Errorf("expected %v, got %v", expected, actual)
   165  		}
   166  		if expected, actual := prevIntA, intA.Get(&st.SV); expected != actual {
   167  			return errors.Errorf("expected %v, got %v", expected, actual)
   168  		}
   169  		if expected, actual := prevDurationA, durationA.Get(&st.SV); expected != actual {
   170  			return errors.Errorf("expected %v, got %v", expected, actual)
   171  		}
   172  		if expected, actual := prevByteSizeA, byteSizeA.Get(&st.SV); expected != actual {
   173  			return errors.Errorf("expected %v, got %v", expected, actual)
   174  		}
   175  		return nil
   176  	})
   177  
   178  	// Deleting a value reverts to default.
   179  	db.Exec(t, deleteQ, strKey)
   180  	testutils.SucceedsSoon(t, func() error {
   181  		if expected, actual := "<default>", strA.Get(&st.SV); expected != actual {
   182  			return errors.Errorf("expected %v, got %v", expected, actual)
   183  		}
   184  		return nil
   185  	})
   186  
   187  }
   188  
   189  func TestSettingsSetAndShow(t *testing.T) {
   190  	defer leaktest.AfterTest(t)()
   191  	// Set up some additional cluster settings to play around with. Note that we
   192  	// need to do this before starting the server, or there will be data races.
   193  	st := cluster.MakeTestingClusterSettings()
   194  	s, rawDB, _ := serverutils.StartServer(t, base.TestServerArgs{Settings: st})
   195  	defer s.Stopper().Stop(context.Background())
   196  
   197  	db := sqlutils.MakeSQLRunner(rawDB)
   198  
   199  	// TODO(dt): add placeholder support to SET and SHOW.
   200  	setQ := `SET CLUSTER SETTING "%s" = %s`
   201  	showQ := `SHOW CLUSTER SETTING "%s"`
   202  
   203  	db.Exec(t, fmt.Sprintf(setQ, strKey, "'via-set'"))
   204  	if expected, actual := "via-set", db.QueryStr(t, fmt.Sprintf(showQ, strKey))[0][0]; expected != actual {
   205  		t.Fatalf("expected %v, got %v", expected, actual)
   206  	}
   207  
   208  	db.Exec(t, fmt.Sprintf(setQ, intKey, "5"))
   209  	if expected, actual := "5", db.QueryStr(t, fmt.Sprintf(showQ, intKey))[0][0]; expected != actual {
   210  		t.Fatalf("expected %v, got %v", expected, actual)
   211  	}
   212  
   213  	db.Exec(t, fmt.Sprintf(setQ, durationKey, "'2h'"))
   214  	if expected, actual := time.Hour*2, durationA.Get(&st.SV); expected != actual {
   215  		t.Fatalf("expected %v, got %v", expected, actual)
   216  	}
   217  	if expected, actual := "02:00:00", db.QueryStr(t, fmt.Sprintf(showQ, durationKey))[0][0]; expected != actual {
   218  		t.Fatalf("expected %v, got %v", expected, actual)
   219  	}
   220  
   221  	db.Exec(t, fmt.Sprintf(setQ, byteSizeKey, "'1500MB'"))
   222  	if expected, actual := int64(1500000000), byteSizeA.Get(&st.SV); expected != actual {
   223  		t.Fatalf("expected %v, got %v", expected, actual)
   224  	}
   225  	if expected, actual := "1.4 GiB", db.QueryStr(t, fmt.Sprintf(showQ, byteSizeKey))[0][0]; expected != actual {
   226  		t.Fatalf("expected %v, got %v", expected, actual)
   227  	}
   228  
   229  	db.Exec(t, fmt.Sprintf(setQ, byteSizeKey, "'1450MB'"))
   230  	if expected, actual := "1.4 GiB", db.QueryStr(t, fmt.Sprintf(showQ, byteSizeKey))[0][0]; expected != actual {
   231  		t.Fatalf("expected %v, got %v", expected, actual)
   232  	}
   233  
   234  	db.ExpectErr(t, `could not parse "a-str" as type int`, fmt.Sprintf(setQ, intKey, "'a-str'"))
   235  
   236  	db.Exec(t, fmt.Sprintf(setQ, enumKey, "2"))
   237  	if expected, actual := int64(2), enumA.Get(&st.SV); expected != actual {
   238  		t.Fatalf("expected %v, got %v", expected, actual)
   239  	}
   240  	if expected, actual := "bar", db.QueryStr(t, fmt.Sprintf(showQ, enumKey))[0][0]; expected != actual {
   241  		t.Fatalf("expected %v, got %v", expected, actual)
   242  	}
   243  
   244  	db.Exec(t, fmt.Sprintf(setQ, enumKey, "'foo'"))
   245  	if expected, actual := int64(1), enumA.Get(&st.SV); expected != actual {
   246  		t.Fatalf("expected %v, got %v", expected, actual)
   247  	}
   248  	if expected, actual := "foo", db.QueryStr(t, fmt.Sprintf(showQ, enumKey))[0][0]; expected != actual {
   249  		t.Fatalf("expected %v, got %v", expected, actual)
   250  	}
   251  
   252  	db.ExpectErr(
   253  		t, `invalid string value 'unknown' for enum setting`,
   254  		fmt.Sprintf(setQ, enumKey, "'unknown'"),
   255  	)
   256  
   257  	db.ExpectErr(t, `invalid integer value '7' for enum setting`, fmt.Sprintf(setQ, enumKey, "7"))
   258  
   259  	db.Exec(t, `CREATE USER testuser`)
   260  	pgURL, cleanupFunc := sqlutils.PGUrl(t, s.ServingSQLAddr(), t.Name(), url.User("testuser"))
   261  	defer cleanupFunc()
   262  	testuser, err := gosql.Open("postgres", pgURL.String())
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  	defer testuser.Close()
   267  
   268  	if _, err := testuser.Exec(`SET CLUSTER SETTING foo = 'bar'`); !testutils.IsError(err,
   269  		`only users with the admin role are allowed to SET CLUSTER SETTING`,
   270  	) {
   271  		t.Fatal(err)
   272  	}
   273  	if _, err := testuser.Exec(`SHOW CLUSTER SETTING foo`); !testutils.IsError(err,
   274  		`only users with the admin role are allowed to SHOW CLUSTER SETTING`,
   275  	) {
   276  		t.Fatal(err)
   277  	}
   278  	if _, err := testuser.Exec(`SHOW ALL CLUSTER SETTINGS`); !testutils.IsError(err,
   279  		`only users with the admin role are allowed to SHOW CLUSTER SETTINGS`,
   280  	) {
   281  		t.Fatal(err)
   282  	}
   283  	if _, err := testuser.Exec(`SHOW CLUSTER SETTINGS`); !testutils.IsError(err,
   284  		`only users with the admin role are allowed to SHOW CLUSTER SETTINGS`,
   285  	) {
   286  		t.Fatal(err)
   287  	}
   288  	if _, err := testuser.Exec(`SELECT * FROM crdb_internal.cluster_settings`); !testutils.IsError(err,
   289  		`only users with the admin role are allowed to read crdb_internal.cluster_settings`,
   290  	) {
   291  		t.Fatal(err)
   292  	}
   293  }
   294  
   295  func TestSettingsShowAll(t *testing.T) {
   296  	defer leaktest.AfterTest(t)()
   297  
   298  	// Set up some additional cluster settings to play around with. Note that we
   299  	// need to do this before starting the server, or there will be data races.
   300  	st := cluster.MakeTestingClusterSettings()
   301  
   302  	s, rawDB, _ := serverutils.StartServer(t, base.TestServerArgs{Settings: st})
   303  	defer s.Stopper().Stop(context.Background())
   304  
   305  	db := sqlutils.MakeSQLRunner(rawDB)
   306  
   307  	rows := db.QueryStr(t, "SHOW ALL CLUSTER SETTINGS")
   308  	if len(rows) < 2 {
   309  		t.Fatalf("show all returned too few rows (%d)", len(rows))
   310  	}
   311  	const expColumns = 5
   312  	if len(rows[0]) != expColumns {
   313  		t.Fatalf("show all must return %d columns, found %d", expColumns, len(rows[0]))
   314  	}
   315  	hasIntKey := false
   316  	hasStrKey := false
   317  	for _, row := range rows {
   318  		switch row[0] {
   319  		case strKey:
   320  			hasStrKey = true
   321  		case intKey:
   322  			hasIntKey = true
   323  		}
   324  	}
   325  	if !hasIntKey || !hasStrKey {
   326  		t.Fatalf("show all did not find the test keys: %q", rows)
   327  	}
   328  }