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  }