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 }