github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/api/v2/capture.go (about)

     1  // Copyright 2023 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package v2
    15  
    16  import (
    17  	"net/http"
    18  
    19  	"github.com/gin-gonic/gin"
    20  	"github.com/pingcap/tiflow/cdc/api"
    21  	cerror "github.com/pingcap/tiflow/pkg/errors"
    22  )
    23  
    24  const apiOpVarCaptureID = "capture_id"
    25  
    26  // drainCapture remove all tables at the given capture.
    27  func (h *OpenAPIV2) drainCapture(c *gin.Context) {
    28  	captureID := c.Param(apiOpVarCaptureID)
    29  
    30  	ctx := c.Request.Context()
    31  	captures, err := h.capture.StatusProvider().GetCaptures(ctx)
    32  	if err != nil {
    33  		_ = c.Error(err)
    34  		return
    35  	}
    36  
    37  	// drain capture only work if there is at least two alive captures,
    38  	// it cannot work properly if it has only one capture.
    39  	if len(captures) <= 1 {
    40  		_ = c.Error(cerror.ErrSchedulerRequestFailed.
    41  			GenWithStackByArgs("only one capture alive"))
    42  		return
    43  	}
    44  
    45  	target := captureID
    46  	checkCaptureFound := func() bool {
    47  		// make sure the target capture exist
    48  		for _, capture := range captures {
    49  			if capture.ID == target {
    50  				return true
    51  			}
    52  		}
    53  		return false
    54  	}
    55  
    56  	if !checkCaptureFound() {
    57  		_ = c.Error(cerror.ErrCaptureNotExist.GenWithStackByArgs(target))
    58  		return
    59  	}
    60  
    61  	// only owner handle api request, so this must be the owner.
    62  	ownerInfo, err := h.capture.Info()
    63  	if err != nil {
    64  		_ = c.Error(err)
    65  		return
    66  	}
    67  
    68  	if ownerInfo.ID == target {
    69  		_ = c.Error(cerror.ErrSchedulerRequestFailed.
    70  			GenWithStackByArgs("cannot drain the owner"))
    71  		return
    72  	}
    73  
    74  	resp, err := api.HandleOwnerDrainCapture(ctx, h.capture, target)
    75  	if err != nil {
    76  		_ = c.AbortWithError(http.StatusServiceUnavailable, err)
    77  		return
    78  	}
    79  
    80  	c.JSON(http.StatusAccepted, resp)
    81  }
    82  
    83  // listCaptures lists all captures
    84  // @Summary List captures
    85  // @Description list all captures in cdc cluster
    86  // @Tags capture,v2
    87  // @Produce json
    88  // @Success 200 {array} Capture
    89  // @Failure 500,400 {object} model.HTTPError
    90  // @Router	/api/v2/captures [get]
    91  func (h *OpenAPIV2) listCaptures(c *gin.Context) {
    92  	ctx := c.Request.Context()
    93  	controller, err := h.capture.GetController()
    94  	if err != nil {
    95  		_ = c.Error(err)
    96  		return
    97  	}
    98  	captureInfos, err := controller.GetCaptures(ctx)
    99  	if err != nil {
   100  		_ = c.Error(err)
   101  		return
   102  	}
   103  	info, err := h.capture.Info()
   104  	if err != nil {
   105  		_ = c.Error(err)
   106  		return
   107  	}
   108  	ownerID := info.ID
   109  
   110  	etcdClient := h.capture.GetEtcdClient()
   111  
   112  	captures := make([]Capture, 0, len(captureInfos))
   113  	for _, c := range captureInfos {
   114  		isOwner := c.ID == ownerID
   115  		captures = append(captures,
   116  			Capture{
   117  				ID:            c.ID,
   118  				IsOwner:       isOwner,
   119  				AdvertiseAddr: c.AdvertiseAddr,
   120  				ClusterID:     etcdClient.GetClusterID(),
   121  			})
   122  	}
   123  	resp := &ListResponse[Capture]{
   124  		Total: len(captureInfos),
   125  		Items: captures,
   126  	}
   127  	c.JSON(http.StatusOK, resp)
   128  }