vitess.io/vitess@v0.16.2/go/test/endtoend/tabletmanager/commands_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 tabletmanager 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "reflect" 24 "testing" 25 "time" 26 27 "vitess.io/vitess/go/test/endtoend/utils" 28 29 "github.com/stretchr/testify/require" 30 "github.com/tidwall/gjson" 31 32 "github.com/stretchr/testify/assert" 33 34 "vitess.io/vitess/go/mysql" 35 "vitess.io/vitess/go/test/endtoend/cluster" 36 ) 37 38 var ( 39 getSchemaT1Results8030 = "CREATE TABLE `t1` (\n `id` bigint NOT NULL,\n `value` varchar(16) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3" 40 getSchemaT1Results80 = "CREATE TABLE `t1` (\n `id` bigint NOT NULL,\n `value` varchar(16) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8" 41 getSchemaT1Results57 = "CREATE TABLE `t1` (\n `id` bigint(20) NOT NULL,\n `value` varchar(16) DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8" 42 getSchemaV1Results = fmt.Sprintf("CREATE ALGORITHM=UNDEFINED DEFINER=`%s`@`%s` SQL SECURITY DEFINER VIEW {{.DatabaseName}}.`v1` AS select {{.DatabaseName}}.`t1`.`id` AS `id`,{{.DatabaseName}}.`t1`.`value` AS `value` from {{.DatabaseName}}.`t1`", username, hostname) 43 ) 44 45 // TabletCommands tests the basic tablet commands 46 func TestTabletCommands(t *testing.T) { 47 defer cluster.PanicHandler(t) 48 ctx := context.Background() 49 50 conn, err := mysql.Connect(ctx, &primaryTabletParams) 51 require.Nil(t, err) 52 defer conn.Close() 53 54 replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) 55 require.Nil(t, err) 56 defer replicaConn.Close() 57 58 // Sanity Check 59 utils.Exec(t, conn, "delete from t1") 60 utils.Exec(t, conn, "insert into t1(id, value) values(1,'a'), (2,'b')") 61 checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")]]`) 62 63 // make sure direct dba queries work 64 sql := "select * from t1" 65 result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ExecuteFetchAsDba", "--", "--json", primaryTablet.Alias, sql) 66 require.Nil(t, err) 67 assertExecuteFetch(t, result) 68 69 // check Ping / RefreshState / RefreshStateByShard 70 err = clusterInstance.VtctlclientProcess.ExecuteCommand("Ping", primaryTablet.Alias) 71 require.Nil(t, err, "error should be Nil") 72 73 err = clusterInstance.VtctlclientProcess.ExecuteCommand("RefreshState", primaryTablet.Alias) 74 require.Nil(t, err, "error should be Nil") 75 76 err = clusterInstance.VtctlclientProcess.ExecuteCommand("RefreshStateByShard", keyspaceShard) 77 require.Nil(t, err, "error should be Nil") 78 79 err = clusterInstance.VtctlclientProcess.ExecuteCommand("RefreshStateByShard", "--", "--cells="+cell, keyspaceShard) 80 require.Nil(t, err, "error should be Nil") 81 82 // Check basic actions. 83 err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadOnly", primaryTablet.Alias) 84 require.Nil(t, err, "error should be Nil") 85 qr := utils.Exec(t, conn, "show variables like 'read_only'") 86 got := fmt.Sprintf("%v", qr.Rows) 87 want := "[[VARCHAR(\"read_only\") VARCHAR(\"ON\")]]" 88 assert.Equal(t, want, got) 89 90 err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", primaryTablet.Alias) 91 require.Nil(t, err, "error should be Nil") 92 qr = utils.Exec(t, conn, "show variables like 'read_only'") 93 got = fmt.Sprintf("%v", qr.Rows) 94 want = "[[VARCHAR(\"read_only\") VARCHAR(\"OFF\")]]" 95 assert.Equal(t, want, got) 96 97 err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") 98 require.Nil(t, err, "error should be Nil") 99 err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate", "--", "--ping-tablets=true") 100 require.Nil(t, err, "error should be Nil") 101 102 err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateKeyspace", keyspaceName) 103 require.Nil(t, err, "error should be Nil") 104 err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateKeyspace", "--", "--ping-tablets=true", keyspaceName) 105 require.Nil(t, err, "error should be Nil") 106 107 err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateShard", "--", "--ping-tablets=false", keyspaceShard) 108 require.Nil(t, err, "error should be Nil") 109 110 err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateShard", "--", "--ping-tablets=true", keyspaceShard) 111 require.Nil(t, err, "error should be Nil") 112 113 } 114 115 func assertExcludeFields(t *testing.T, qr string) { 116 resultMap := make(map[string]any) 117 err := json.Unmarshal([]byte(qr), &resultMap) 118 require.Nil(t, err) 119 120 rows := resultMap["rows"].([]any) 121 assert.Equal(t, 2, len(rows)) 122 123 fields := resultMap["fields"] 124 assert.NotContainsf(t, fields, "name", "name should not be in field list") 125 } 126 127 func assertExecuteFetch(t *testing.T, qr string) { 128 resultMap := make(map[string]any) 129 err := json.Unmarshal([]byte(qr), &resultMap) 130 require.Nil(t, err) 131 132 rows := reflect.ValueOf(resultMap["rows"]) 133 got := rows.Len() 134 want := int(2) 135 assert.Equal(t, want, got) 136 137 fields := reflect.ValueOf(resultMap["fields"]) 138 got = fields.Len() 139 want = int(2) 140 assert.Equal(t, want, got) 141 } 142 143 // ActionAndTimeout test 144 func TestActionAndTimeout(t *testing.T) { 145 146 defer cluster.PanicHandler(t) 147 err := clusterInstance.VtctlclientProcess.ExecuteCommand("Sleep", primaryTablet.Alias, "5s") 148 require.Nil(t, err) 149 time.Sleep(1 * time.Second) 150 151 // try a frontend RefreshState that should timeout as the tablet is busy running the other one 152 err = clusterInstance.VtctlclientProcess.ExecuteCommand("RefreshState", "--", primaryTablet.Alias, "--wait-time", "2s") 153 assert.Error(t, err, "timeout as tablet is in Sleep") 154 } 155 156 func TestHook(t *testing.T) { 157 // test a regular program works 158 defer cluster.PanicHandler(t) 159 runHookAndAssert(t, []string{ 160 "ExecuteHook", "--", primaryTablet.Alias, "test.sh", "--flag1", "--param1=hello"}, "0", false, "") 161 162 // test stderr output 163 runHookAndAssert(t, []string{ 164 "ExecuteHook", "--", primaryTablet.Alias, "test.sh", "--to-stderr"}, "0", false, "ERR: --to-stderr\n") 165 166 // test commands that fail 167 runHookAndAssert(t, []string{ 168 "ExecuteHook", "--", primaryTablet.Alias, "test.sh", "--exit-error"}, "1", false, "ERROR: exit status 1\n") 169 170 // test hook that is not present 171 runHookAndAssert(t, []string{ 172 "ExecuteHook", "--", primaryTablet.Alias, "not_here.sh", "--exit-error"}, "-1", false, "missing hook") 173 174 // test hook with invalid name 175 176 runHookAndAssert(t, []string{ 177 "ExecuteHook", "--", primaryTablet.Alias, "/bin/ls"}, "-1", true, "hook name cannot have") 178 } 179 180 func runHookAndAssert(t *testing.T, params []string, expectedStatus string, expectedError bool, expectedStderr string) { 181 182 hr, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput(params...) 183 if expectedError { 184 assert.Error(t, err, "Expected error") 185 } else { 186 require.Nil(t, err) 187 188 resultMap := make(map[string]any) 189 err = json.Unmarshal([]byte(hr), &resultMap) 190 require.Nil(t, err) 191 192 exitStatus := reflect.ValueOf(resultMap["ExitStatus"]).Float() 193 status := fmt.Sprintf("%.0f", exitStatus) 194 assert.Equal(t, expectedStatus, status) 195 196 stderr := reflect.ValueOf(resultMap["Stderr"]).String() 197 assert.Contains(t, stderr, expectedStderr) 198 } 199 200 } 201 202 func TestShardReplicationFix(t *testing.T) { 203 // make sure the replica is in the replication graph, 2 nodes: 1 primary, 1 replica 204 defer cluster.PanicHandler(t) 205 result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell, keyspaceShard) 206 require.Nil(t, err, "error should be Nil") 207 assertNodeCount(t, result, int(3)) 208 209 // Manually add a bogus entry to the replication graph, and check it is removed by ShardReplicationFix 210 err = clusterInstance.VtctlclientProcess.ExecuteCommand("ShardReplicationAdd", keyspaceShard, fmt.Sprintf("%s-9000", cell)) 211 require.Nil(t, err, "error should be Nil") 212 213 result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell, keyspaceShard) 214 require.Nil(t, err, "error should be Nil") 215 assertNodeCount(t, result, int(4)) 216 217 err = clusterInstance.VtctlclientProcess.ExecuteCommand("ShardReplicationFix", cell, keyspaceShard) 218 require.Nil(t, err, "error should be Nil") 219 result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell, keyspaceShard) 220 require.Nil(t, err, "error should be Nil") 221 assertNodeCount(t, result, int(3)) 222 } 223 224 func TestGetSchema(t *testing.T) { 225 defer cluster.PanicHandler(t) 226 227 res, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetSchema", "--", 228 "--include-views", "--tables", "t1,v1", 229 fmt.Sprintf("%s-%d", clusterInstance.Cell, primaryTablet.TabletUID)) 230 require.Nil(t, err) 231 232 t1Create := gjson.Get(res, "table_definitions.#(name==\"t1\").schema") 233 assert.Contains(t, []string{getSchemaT1Results8030, getSchemaT1Results80, getSchemaT1Results57}, t1Create.String()) 234 v1Create := gjson.Get(res, "table_definitions.#(name==\"v1\").schema") 235 assert.Equal(t, getSchemaV1Results, v1Create.String()) 236 } 237 238 func assertNodeCount(t *testing.T, result string, want int) { 239 resultMap := make(map[string]any) 240 err := json.Unmarshal([]byte(result), &resultMap) 241 require.Nil(t, err) 242 243 nodes := reflect.ValueOf(resultMap["nodes"]) 244 got := nodes.Len() 245 assert.Equal(t, want, got) 246 }