vitess.io/vitess@v0.16.2/go/vt/vtadmin/http/shards.go (about) 1 /* 2 Copyright 2021 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 http 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 24 "github.com/gorilla/mux" 25 "k8s.io/apimachinery/pkg/util/sets" 26 27 "vitess.io/vitess/go/vt/topo/topoproto" 28 "vitess.io/vitess/go/vt/vtadmin/errors" 29 30 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 31 vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" 32 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 33 ) 34 35 // CreateShard implements the http wrapper for POST /shards/{cluster_id}. 36 func CreateShard(ctx context.Context, r Request, api *API) *JSONResponse { 37 vars := mux.Vars(r.Request) 38 decoder := json.NewDecoder(r.Body) 39 defer r.Body.Close() 40 41 var req vtctldatapb.CreateShardRequest 42 if err := decoder.Decode(&req); err != nil { 43 return NewJSONResponse(nil, &errors.BadRequest{ 44 Err: err, 45 }) 46 } 47 48 resp, err := api.server.CreateShard(ctx, &vtadminpb.CreateShardRequest{ 49 ClusterId: vars["cluster_id"], 50 Options: &req, 51 }) 52 return NewJSONResponse(resp, err) 53 } 54 55 // DeleteShards implements the http wrapper for DELETE /shards/{cluster_id}. 56 // Query params: 57 // - keyspace_shard: required, repeated list of keyspace/shards to delete. 58 // - recursive: bool 59 // - even_if_serving: bool 60 func DeleteShards(ctx context.Context, r Request, api *API) *JSONResponse { 61 vars := mux.Vars(r.Request) 62 recursive, err := r.ParseQueryParamAsBool("recursive", false) 63 if err != nil { 64 return NewJSONResponse(nil, err) 65 } 66 67 evenIfServing, err := r.ParseQueryParamAsBool("even_if_serving", false) 68 if err != nil { 69 return NewJSONResponse(nil, err) 70 } 71 72 shardList := r.URL.Query()["keyspace_shard"] 73 shardList = sets.List(sets.New[string](shardList...)) 74 shards := make([]*vtctldatapb.Shard, len(shardList)) 75 for i, kss := range shardList { 76 ks, shard, err := topoproto.ParseKeyspaceShard(kss) 77 if err != nil { 78 return NewJSONResponse(nil, &errors.BadRequest{ 79 Err: fmt.Errorf("%w: parsing %s at position %d", err, kss, i), 80 }) 81 } 82 83 shards[i] = &vtctldatapb.Shard{ 84 Keyspace: ks, 85 Name: shard, 86 Shard: &topodatapb.Shard{}, 87 } 88 } 89 90 if len(shards) == 0 { 91 return NewJSONResponse(nil, &errors.BadRequest{ 92 Err: fmt.Errorf("must specify at least one keyspace_shard to delete (got %+v)", shardList), 93 }) 94 } 95 96 resp, err := api.server.DeleteShards(ctx, &vtadminpb.DeleteShardsRequest{ 97 ClusterId: vars["cluster_id"], 98 Options: &vtctldatapb.DeleteShardsRequest{ 99 Shards: shards, 100 Recursive: recursive, 101 EvenIfServing: evenIfServing, 102 }, 103 }) 104 return NewJSONResponse(resp, err) 105 } 106 107 // EmergencyFailoverShard implements the http wrapper for 108 // POST /shard/{cluster_id}/{keyspace}/{shard}/emergency_failover. 109 // 110 // Query params: none 111 // 112 // POST body is unmarshalled as vtctldatapb.EmergencyReparentShardRequest, but 113 // the Keyspace and Shard fields are ignored (coming instead from the route). 114 func EmergencyFailoverShard(ctx context.Context, r Request, api *API) *JSONResponse { 115 decoder := json.NewDecoder(r.Body) 116 defer r.Body.Close() 117 118 var options vtctldatapb.EmergencyReparentShardRequest 119 if err := decoder.Decode(&options); err != nil { 120 return NewJSONResponse(nil, &errors.BadRequest{ 121 Err: err, 122 }) 123 } 124 125 vars := r.Vars() 126 options.Keyspace = vars["keyspace"] 127 options.Shard = vars["shard"] 128 129 result, err := api.server.EmergencyFailoverShard(ctx, &vtadminpb.EmergencyFailoverShardRequest{ 130 ClusterId: vars["cluster_id"], 131 Options: &options, 132 }) 133 return NewJSONResponse(result, err) 134 } 135 136 // PlannedFailoverShard implements the http wrapper for 137 // POST /shard/{cluster_id}/{keyspace}/{shard}/planned_failover. 138 // 139 // Query params: none 140 // 141 // POST body is unmarshalled as vtctldatapb.PlannedReparentShardRequest, but 142 // the Keyspace and Shard fields are ignored (coming instead from the route). 143 func PlannedFailoverShard(ctx context.Context, r Request, api *API) *JSONResponse { 144 decoder := json.NewDecoder(r.Body) 145 defer r.Body.Close() 146 147 var options vtctldatapb.PlannedReparentShardRequest 148 if err := decoder.Decode(&options); err != nil { 149 return NewJSONResponse(nil, &errors.BadRequest{ 150 Err: err, 151 }) 152 } 153 154 vars := r.Vars() 155 options.Keyspace = vars["keyspace"] 156 options.Shard = vars["shard"] 157 158 result, err := api.server.PlannedFailoverShard(ctx, &vtadminpb.PlannedFailoverShardRequest{ 159 ClusterId: vars["cluster_id"], 160 Options: &options, 161 }) 162 return NewJSONResponse(result, err) 163 } 164 165 // ReloadSchemaShard implements the http wrapper for 166 // PUT /shard/{cluster_id}/{keyspace}/{shard}/reload_schema_shard 167 // 168 // Query params: none 169 // 170 // Body params: 171 // - wait_position: string 172 // - include_primary: bool 173 // - concurrency: uint32 174 func ReloadSchemaShard(ctx context.Context, r Request, api *API) *JSONResponse { 175 decoder := json.NewDecoder(r.Body) 176 defer r.Body.Close() 177 178 var params struct { 179 WaitPosition string `json:"wait_position"` 180 IncludePrimary bool `json:"include_primary"` 181 Concurrency uint32 `json:"concurrency"` 182 } 183 184 if err := decoder.Decode(¶ms); err != nil { 185 return NewJSONResponse(nil, &errors.BadRequest{ 186 Err: err, 187 }) 188 } 189 190 vars := r.Vars() 191 192 result, err := api.server.ReloadSchemaShard(ctx, &vtadminpb.ReloadSchemaShardRequest{ 193 ClusterId: vars["cluster_id"], 194 Keyspace: vars["keyspace"], 195 Shard: vars["shard"], 196 Concurrency: params.Concurrency, 197 IncludePrimary: params.IncludePrimary, 198 WaitPosition: params.WaitPosition, 199 }) 200 return NewJSONResponse(result, err) 201 } 202 203 // ValidateShard implements the http wrapper for 204 // PUT /shard/{cluster_id}/{keyspace}/{shard}/validate 205 // 206 // Query params: none 207 // 208 // Body params: 209 // - ping_tablets: bool 210 func ValidateShard(ctx context.Context, r Request, api *API) *JSONResponse { 211 decoder := json.NewDecoder(r.Body) 212 defer r.Body.Close() 213 214 var params struct { 215 PingTablets bool `json:"ping_tablets"` 216 } 217 218 if err := decoder.Decode(¶ms); err != nil { 219 return NewJSONResponse(nil, &errors.BadRequest{ 220 Err: err, 221 }) 222 } 223 224 vars := r.Vars() 225 226 result, err := api.server.ValidateShard(ctx, &vtadminpb.ValidateShardRequest{ 227 ClusterId: vars["cluster_id"], 228 Keyspace: vars["keyspace"], 229 Shard: vars["shard"], 230 PingTablets: params.PingTablets, 231 }) 232 233 return NewJSONResponse(result, err) 234 } 235 236 // ValidateVersionShard implements the http wrapper for 237 // PUT /shard/{cluster_id}/{keyspace}/{shard}/validate_version 238 // 239 // Query params: none 240 // 241 // Body params: none 242 func ValidateVersionShard(ctx context.Context, r Request, api *API) *JSONResponse { 243 vars := r.Vars() 244 245 result, err := api.server.ValidateVersionShard(ctx, &vtadminpb.ValidateVersionShardRequest{ 246 ClusterId: vars["cluster_id"], 247 Keyspace: vars["keyspace"], 248 Shard: vars["shard"], 249 }) 250 251 return NewJSONResponse(result, err) 252 }