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 }