vitess.io/vitess@v0.16.2/go/vt/topo/shard_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package topo 18 19 import ( 20 "context" 21 "reflect" 22 "testing" 23 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 27 "vitess.io/vitess/go/test/utils" 28 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 29 ) 30 31 // This file tests the shard related object functionnalities. 32 33 func TestAddCells(t *testing.T) { 34 var cells []string 35 36 // no restriction + no restriction -> no restrictions 37 cells = addCells(cells, nil) 38 if cells != nil { 39 t.Fatalf("addCells(no restriction)+no restriction should be no restriction") 40 } 41 42 // no restriction + cells -> no restrictions 43 cells = addCells(cells, []string{"c1", "c2"}) 44 if cells != nil { 45 t.Fatalf("addCells(no restriction)+restriction should be no restriction") 46 } 47 48 // cells + no restriction -> no restrictions 49 cells = []string{"c1", "c2"} 50 cells = addCells(cells, nil) 51 if cells != nil { 52 t.Fatalf("addCells(restriction)+no restriction should be no restriction") 53 } 54 55 // cells + cells -> union 56 cells = []string{"c1", "c2"} 57 cells = addCells(cells, []string{"c2", "c3"}) 58 if !reflect.DeepEqual(cells, []string{"c1", "c2", "c3"}) { 59 t.Fatalf("addCells(restriction)+restriction failed: got %v", cells) 60 } 61 } 62 63 func TestRemoveCellsFromList(t *testing.T) { 64 var cells []string 65 allCells := []string{"first", "second", "third"} 66 67 // remove from empty list should return allCells - what we remove 68 cells = removeCellsFromList([]string{"second"}, allCells) 69 if !reflect.DeepEqual(cells, []string{"first", "third"}) { 70 t.Fatalf("removeCells(full)-second failed: got %v", allCells) 71 } 72 73 // removethe next two cells, should return empty list 74 cells = removeCellsFromList(cells, []string{"first", "third"}) 75 if len(cells) != 0 { 76 t.Fatalf("removeCells(full)-first-third is not empty: %v", cells) 77 } 78 } 79 80 func TestRemoveCells(t *testing.T) { 81 var cells []string 82 allCells := []string{"first", "second", "third"} 83 84 // remove from empty list should return allCells - what we remove 85 var emptyResult bool 86 cells, emptyResult = removeCells(cells, []string{"second"}, allCells) 87 if emptyResult || !reflect.DeepEqual(cells, []string{"first", "third"}) { 88 t.Fatalf("removeCells(full)-second failed: got %v", cells) 89 } 90 91 // removethe next two cells, should return empty list 92 cells, emptyResult = removeCells(cells, []string{"first", "third"}, allCells) 93 if !emptyResult { 94 t.Fatalf("removeCells(full)-first-third is not empty: %v", cells) 95 } 96 } 97 98 func lockedKeyspaceContext(keyspace string) context.Context { 99 ctx := context.Background() 100 return context.WithValue(ctx, locksKey, &locksInfo{ 101 info: map[string]*lockInfo{ 102 // An empty entry is good enough for this. 103 keyspace: {}, 104 }, 105 }) 106 } 107 108 func addToDenyList(ctx context.Context, si *ShardInfo, tabletType topodatapb.TabletType, cells, tables []string) error { 109 if err := si.UpdateSourceDeniedTables(ctx, tabletType, cells, false, tables); err != nil { 110 return err 111 } 112 return nil 113 } 114 115 func removeFromDenyList(ctx context.Context, si *ShardInfo, tabletType topodatapb.TabletType, cells, tables []string) error { 116 if err := si.UpdateSourceDeniedTables(ctx, tabletType, cells, true, tables); err != nil { 117 return err 118 } 119 return nil 120 } 121 122 func validateDenyList(t *testing.T, si *ShardInfo, tabletType topodatapb.TabletType, cells, tables []string) { 123 tc := si.GetTabletControl(tabletType) 124 require.ElementsMatch(t, tc.Cells, cells) 125 require.ElementsMatch(t, tc.DeniedTables, tables) 126 } 127 128 func TestUpdateSourcePrimaryDeniedTables(t *testing.T) { 129 primary := topodatapb.TabletType_PRIMARY 130 si := NewShardInfo("ks", "sh", &topodatapb.Shard{}, nil) 131 ctx := lockedKeyspaceContext("ks") 132 t1, t2, t3, t4 := "t1", "t2", "t3", "t4" 133 tables1 := []string{t1, t2} 134 tables2 := []string{t3, t4} 135 136 require.NoError(t, addToDenyList(ctx, si, primary, nil, tables1)) 137 validateDenyList(t, si, primary, nil, tables1) 138 139 require.NoError(t, addToDenyList(ctx, si, primary, nil, tables2)) 140 validateDenyList(t, si, primary, nil, append(tables1, tables2...)) 141 142 require.Error(t, addToDenyList(ctx, si, primary, nil, tables2), dlTablesAlreadyPresent) 143 require.Error(t, addToDenyList(ctx, si, primary, nil, []string{t1}), dlTablesAlreadyPresent) 144 145 require.NoError(t, removeFromDenyList(ctx, si, primary, nil, tables2)) 146 validateDenyList(t, si, primary, nil, tables1) 147 148 require.Error(t, removeFromDenyList(ctx, si, primary, nil, tables2), dlTablesNotPresent) 149 require.Error(t, removeFromDenyList(ctx, si, primary, nil, []string{t3}), dlTablesNotPresent) 150 validateDenyList(t, si, primary, nil, tables1) 151 152 require.NoError(t, removeFromDenyList(ctx, si, primary, nil, []string{t1})) 153 require.NoError(t, removeFromDenyList(ctx, si, primary, nil, []string{t2})) 154 require.Nil(t, si.GetTabletControl(primary)) 155 156 require.Error(t, addToDenyList(ctx, si, primary, []string{"cell"}, tables1), dlNoCellsForPrimary) 157 } 158 159 func TestUpdateSourceDeniedTables(t *testing.T) { 160 si := NewShardInfo("ks", "sh", &topodatapb.Shard{}, nil) 161 162 // check we enforce the keyspace lock 163 ctx := context.Background() 164 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, nil, false, nil); err == nil || err.Error() != "keyspace ks is not locked (no locksInfo)" { 165 t.Fatalf("unlocked keyspace produced wrong error: %v", err) 166 } 167 ctx = lockedKeyspaceContext("ks") 168 169 // add one cell 170 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, []string{"first"}, false, []string{"t1", "t2"}); err != nil || !reflect.DeepEqual(si.TabletControls, []*topodatapb.Shard_TabletControl{ 171 { 172 TabletType: topodatapb.TabletType_RDONLY, 173 Cells: []string{"first"}, 174 DeniedTables: []string{"t1", "t2"}, 175 }, 176 }) { 177 t.Fatalf("one cell add failed: %v", si) 178 } 179 180 // remove that cell, going back 181 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, []string{"first"}, true, nil); err != nil || len(si.TabletControls) != 0 { 182 t.Fatalf("going back should have remove the record: %v", si) 183 } 184 185 // re-add a cell, then another with different table list to 186 // make sure it fails 187 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, []string{"first"}, false, []string{"t1", "t2"}); err != nil { 188 t.Fatalf("one cell add failed: %v", si) 189 } 190 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, []string{"second"}, false, []string{"t2", "t3"}); err == nil || err.Error() != "trying to use two different sets of denied tables for shard ks/sh: [t1 t2] and [t2 t3]" { 191 t.Fatalf("different table list should fail: %v", err) 192 } 193 // add another cell, see the list grow 194 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, []string{"second"}, false, []string{"t1", "t2"}); err != nil || !reflect.DeepEqual(si.TabletControls, []*topodatapb.Shard_TabletControl{ 195 { 196 TabletType: topodatapb.TabletType_RDONLY, 197 Cells: []string{"first", "second"}, 198 DeniedTables: []string{"t1", "t2"}, 199 }, 200 }) { 201 t.Fatalf("second cell add failed: %v", si) 202 } 203 204 // add all cells, see the list grow to all 205 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, []string{"first", "second", "third"}, false, []string{"t1", "t2"}); err != nil || !reflect.DeepEqual(si.TabletControls, []*topodatapb.Shard_TabletControl{ 206 { 207 TabletType: topodatapb.TabletType_RDONLY, 208 Cells: []string{"first", "second", "third"}, 209 DeniedTables: []string{"t1", "t2"}, 210 }, 211 }) { 212 t.Fatalf("all cells add failed: %v", si) 213 } 214 215 // remove one cell from the full list 216 if err := si.UpdateSourceDeniedTables(ctx, topodatapb.TabletType_RDONLY, []string{"second"}, true, []string{"t1", "t2"}); err != nil || !reflect.DeepEqual(si.TabletControls, []*topodatapb.Shard_TabletControl{ 217 { 218 TabletType: topodatapb.TabletType_RDONLY, 219 Cells: []string{"first", "third"}, 220 DeniedTables: []string{"t1", "t2"}, 221 }, 222 }) { 223 t.Fatalf("one cell removal from all failed: %v", si) 224 } 225 } 226 227 func TestValidateShardName(t *testing.T) { 228 t.Parallel() 229 230 cases := []struct { 231 name string 232 expectedRange *topodatapb.KeyRange 233 valid bool 234 }{ 235 { 236 name: "0", 237 valid: true, 238 }, 239 { 240 name: "-80", 241 expectedRange: &topodatapb.KeyRange{ 242 Start: nil, 243 End: []byte{0x80}, 244 }, 245 valid: true, 246 }, 247 { 248 name: "40-80", 249 expectedRange: &topodatapb.KeyRange{ 250 Start: []byte{0x40}, 251 End: []byte{0x80}, 252 }, 253 valid: true, 254 }, 255 { 256 name: "foo-bar", 257 valid: false, 258 }, 259 { 260 name: "a/b", 261 valid: false, 262 }, 263 } 264 265 for _, tcase := range cases { 266 tcase := tcase 267 t.Run(tcase.name, func(t *testing.T) { 268 t.Parallel() 269 270 _, kr, err := ValidateShardName(tcase.name) 271 if !tcase.valid { 272 assert.Error(t, err, "expected %q to be an invalid shard name", tcase.name) 273 return 274 } 275 276 require.NoError(t, err, "expected %q to be a valid shard name, got error: %v", tcase.name, err) 277 utils.MustMatch(t, tcase.expectedRange, kr) 278 }) 279 } 280 }