vitess.io/vitess@v0.16.2/go/test/endtoend/vtorc/api/api_test.go (about)

     1  /*
     2  Copyright 2022 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 api
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"vitess.io/vitess/go/test/endtoend/cluster"
    28  	"vitess.io/vitess/go/test/endtoend/vtorc/utils"
    29  )
    30  
    31  // make an api call to /api/problems endpoint
    32  // and verify the output
    33  func TestProblemsAPI(t *testing.T) {
    34  	defer cluster.PanicHandler(t)
    35  	utils.SetupVttabletsAndVTOrcs(t, clusterInfo, 2, 1, nil, cluster.VTOrcConfiguration{
    36  		PreventCrossDataCenterPrimaryFailover: true,
    37  		RecoveryPeriodBlockSeconds:            5,
    38  	}, 1, "")
    39  	keyspace := &clusterInfo.ClusterInstance.Keyspaces[0]
    40  	shard0 := &keyspace.Shards[0]
    41  	vtorc := clusterInfo.ClusterInstance.VTOrcProcesses[0]
    42  
    43  	// find primary from topo
    44  	primary := utils.ShardPrimaryTablet(t, clusterInfo, keyspace, shard0)
    45  	assert.NotNil(t, primary, "should have elected a primary")
    46  
    47  	// find the replica and rdonly tablet
    48  	var replica, rdonly *cluster.Vttablet
    49  	for _, tablet := range shard0.Vttablets {
    50  		// we know we have only two replica type tablets, so the one not the primary must be the replica
    51  		if tablet.Alias != primary.Alias && tablet.Type == "replica" {
    52  			replica = tablet
    53  		}
    54  		if tablet.Type == "rdonly" {
    55  			rdonly = tablet
    56  		}
    57  	}
    58  	assert.NotNil(t, replica, "could not find replica tablet")
    59  	assert.NotNil(t, rdonly, "could not find rdonly tablet")
    60  
    61  	// check that the replication is setup correctly before we set read-only
    62  	utils.CheckReplication(t, clusterInfo, primary, []*cluster.Vttablet{replica, rdonly}, 10*time.Second)
    63  
    64  	t.Run("Health API", func(t *testing.T) {
    65  		// Check that VTOrc is healthy
    66  		status, resp := utils.MakeAPICall(t, vtorc, "/debug/health")
    67  		assert.Equal(t, 200, status)
    68  		assert.Contains(t, resp, `"Healthy": true,`)
    69  	})
    70  
    71  	t.Run("Liveness API", func(t *testing.T) {
    72  		// Check that VTOrc is live
    73  		status, resp := utils.MakeAPICall(t, vtorc, "/debug/liveness")
    74  		assert.Equal(t, 200, status)
    75  		assert.Empty(t, resp)
    76  	})
    77  
    78  	// Before we disable recoveries, let us wait until VTOrc has fixed all the issues (if any).
    79  	_, _ = utils.MakeAPICallRetry(t, vtorc, "/api/replication-analysis", func(_ int, response string) bool {
    80  		return response != "[]"
    81  	})
    82  
    83  	t.Run("Disable Recoveries API", func(t *testing.T) {
    84  		// Disable recoveries of VTOrc
    85  		status, resp := utils.MakeAPICall(t, vtorc, "/api/disable-global-recoveries")
    86  		assert.Equal(t, 200, status)
    87  		assert.Equal(t, "Global recoveries disabled\n", resp)
    88  	})
    89  
    90  	t.Run("Replication Analysis API", func(t *testing.T) {
    91  		// use vtctlclient to stop replication
    92  		_, err := clusterInfo.ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("StopReplication", replica.Alias)
    93  		require.NoError(t, err)
    94  
    95  		// We know VTOrc won't fix this since we disabled global recoveries!
    96  		// Wait until VTOrc picks up on this issue and verify
    97  		// that we see a not null result on the api/replication-analysis page
    98  		status, resp := utils.MakeAPICallRetry(t, vtorc, "/api/replication-analysis", func(_ int, response string) bool {
    99  			return response == "[]"
   100  		})
   101  		assert.Equal(t, 200, status, resp)
   102  		assert.Contains(t, resp, fmt.Sprintf(`"Port": %d`, replica.MySQLPort))
   103  		assert.Contains(t, resp, `"Analysis": "ReplicationStopped"`)
   104  
   105  		// Verify that filtering also works in the API as intended
   106  		status, resp = utils.MakeAPICall(t, vtorc, "/api/replication-analysis?keyspace=ks&shard=0")
   107  		assert.Equal(t, 200, status, resp)
   108  		assert.Contains(t, resp, fmt.Sprintf(`"Port": %d`, replica.MySQLPort))
   109  
   110  		// Verify that filtering by keyspace also works in the API as intended
   111  		status, resp = utils.MakeAPICall(t, vtorc, "/api/replication-analysis?keyspace=ks")
   112  		assert.Equal(t, 200, status, resp)
   113  		assert.Contains(t, resp, fmt.Sprintf(`"Port": %d`, replica.MySQLPort))
   114  
   115  		// Check that filtering using keyspace and shard works
   116  		status, resp = utils.MakeAPICall(t, vtorc, "/api/replication-analysis?keyspace=ks&shard=80-")
   117  		assert.Equal(t, 200, status, resp)
   118  		assert.Equal(t, "[]", resp)
   119  
   120  		// Check that filtering using just the shard fails
   121  		status, resp = utils.MakeAPICall(t, vtorc, "/api/replication-analysis?shard=0")
   122  		assert.Equal(t, 400, status, resp)
   123  		assert.Equal(t, "Filtering by shard without keyspace isn't supported\n", resp)
   124  	})
   125  
   126  	t.Run("Enable Recoveries API", func(t *testing.T) {
   127  		// Enable recoveries of VTOrc
   128  		status, resp := utils.MakeAPICall(t, vtorc, "/api/enable-global-recoveries")
   129  		assert.Equal(t, 200, status)
   130  		assert.Equal(t, "Global recoveries enabled\n", resp)
   131  
   132  		// Check that replication is indeed repaired by VTOrc, right after we enable the recoveries
   133  		utils.CheckReplication(t, clusterInfo, primary, []*cluster.Vttablet{replica}, 10*time.Second)
   134  	})
   135  
   136  	t.Run("Problems API", func(t *testing.T) {
   137  		// Wait until there are no problems and the api endpoint returns null
   138  		// We need this because we just recovered from a recovery, and it races with this API call
   139  		status, resp := utils.MakeAPICallRetry(t, vtorc, "/api/problems", func(_ int, response string) bool {
   140  			return response != "null"
   141  		})
   142  		assert.Equal(t, 200, status)
   143  		assert.Equal(t, "null", resp)
   144  
   145  		// insert an errant GTID in the replica
   146  		_, err := utils.RunSQL(t, "insert into vt_insert_test(id, msg) values (10173, 'test 178342')", replica, "vt_ks")
   147  		require.NoError(t, err)
   148  
   149  		// Wait until VTOrc picks up on this errant GTID and verify
   150  		// that we see a not null result on the api/problems page
   151  		// and the replica instance is marked as one of the problems
   152  		status, resp = utils.MakeAPICallRetry(t, vtorc, "/api/problems", func(_ int, response string) bool {
   153  			return response == "null"
   154  		})
   155  		assert.Equal(t, 200, status, resp)
   156  		assert.Contains(t, resp, fmt.Sprintf(`"InstanceAlias": "%v"`, replica.Alias))
   157  
   158  		// Check that filtering using keyspace and shard works
   159  		status, resp = utils.MakeAPICall(t, vtorc, "/api/problems?keyspace=ks&shard=0")
   160  		assert.Equal(t, 200, status, resp)
   161  		assert.Contains(t, resp, fmt.Sprintf(`"InstanceAlias": "%v"`, replica.Alias))
   162  
   163  		// Check that filtering using keyspace works
   164  		status, resp = utils.MakeAPICall(t, vtorc, "/api/problems?keyspace=ks")
   165  		assert.Equal(t, 200, status, resp)
   166  		assert.Contains(t, resp, fmt.Sprintf(`"InstanceAlias": "%v"`, replica.Alias))
   167  
   168  		// Check that filtering using keyspace and shard works
   169  		status, resp = utils.MakeAPICall(t, vtorc, "/api/problems?keyspace=ks&shard=80-")
   170  		assert.Equal(t, 200, status, resp)
   171  		assert.Equal(t, "null", resp)
   172  
   173  		// Check that filtering using just the shard fails
   174  		status, resp = utils.MakeAPICall(t, vtorc, "/api/problems?shard=0")
   175  		assert.Equal(t, 400, status, resp)
   176  		assert.Equal(t, "Filtering by shard without keyspace isn't supported\n", resp)
   177  	})
   178  }