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(&params); 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(&params); 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  }