github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/ctl/master/start_stop_validation.go (about)

     1  // Copyright 2022 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 master
    15  
    16  import (
    17  	"context"
    18  	"errors"
    19  	"fmt"
    20  	"os"
    21  
    22  	"github.com/pingcap/tiflow/dm/config"
    23  	"github.com/pingcap/tiflow/dm/ctl/common"
    24  	"github.com/pingcap/tiflow/dm/pb"
    25  	"github.com/pingcap/tiflow/dm/pkg/utils"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  const (
    30  	StartValidationOp = "start"
    31  	StopValidationOp  = "stop"
    32  )
    33  
    34  func NewStartValidationCmd() *cobra.Command {
    35  	cmd := &cobra.Command{
    36  		Use:   "start [--all-task] [task-name]",
    37  		Short: "start to validate the completeness of the data",
    38  		RunE:  startValidation,
    39  	}
    40  	cmd.Flags().Bool("all-task", false, "whether applied to all tasks")
    41  	cmd.Flags().String("mode", "full", "specify the mode of validation: full (default), fast; this flag will be ignored if the validation task has been ever enabled but currently paused")
    42  	cmd.Flags().String("start-time", "", "specify the start time of binlog for validation, e.g. '2021-10-21 00:01:00' or 2021-10-21T00:01:00")
    43  	return cmd
    44  }
    45  
    46  func NewStopValidationCmd() *cobra.Command {
    47  	cmd := &cobra.Command{
    48  		Use:   "stop [--all-task] [task-name]",
    49  		Short: "stop validating the completeness of the data",
    50  		RunE:  stopValidation,
    51  	}
    52  	cmd.Flags().Bool("all-task", false, "whether applied to all tasks")
    53  	return cmd
    54  }
    55  
    56  type validationStartStopArgs struct {
    57  	sources  []string
    58  	allTask  bool
    59  	taskName string
    60  
    61  	mode      string
    62  	startTime string
    63  	// whether user has set --mode or --start-time explicitly
    64  	explicitMode      bool
    65  	explicitStartTime bool
    66  }
    67  
    68  func printUsageAndFailWithMessage(cmd *cobra.Command, errMsg string) error {
    69  	cmd.SetOut(os.Stdout)
    70  	common.PrintCmdUsage(cmd)
    71  	return errors.New(errMsg)
    72  }
    73  
    74  func parseValidationStartStopArgs(cmd *cobra.Command, op string) (validationStartStopArgs, string, bool) {
    75  	var err error
    76  	args := validationStartStopArgs{}
    77  	if args.sources, err = common.GetSourceArgs(cmd); err != nil {
    78  		return args, err.Error(), false
    79  	}
    80  	if args.allTask, err = cmd.Flags().GetBool("all-task"); err != nil {
    81  		return args, err.Error(), false
    82  	}
    83  
    84  	if op == StartValidationOp {
    85  		args.explicitMode = cmd.Flags().Changed("mode")
    86  		if args.explicitMode {
    87  			args.mode, err = cmd.Flags().GetString("mode")
    88  			if err != nil {
    89  				return args, err.Error(), false
    90  			}
    91  			if args.mode != config.ValidationFull && args.mode != config.ValidationFast {
    92  				errMsg := fmt.Sprintf("mode should be either `%s` or `%s`, current is `%s`",
    93  					config.ValidationFull, config.ValidationFast, args.mode)
    94  				return args, errMsg, false
    95  			}
    96  		}
    97  		args.explicitStartTime = cmd.Flags().Changed("start-time")
    98  		if args.explicitStartTime {
    99  			if args.startTime, err = cmd.Flags().GetString("start-time"); err != nil {
   100  				return args, err.Error(), false
   101  			}
   102  			if _, err = utils.ParseStartTime(args.startTime); err != nil {
   103  				return args, "start-time should be in the format like '2006-01-02 15:04:05' or '2006-01-02T15:04:05'", false
   104  			}
   105  		}
   106  	}
   107  
   108  	switch len(cmd.Flags().Args()) {
   109  	case 1:
   110  		args.taskName = cmd.Flags().Arg(0)
   111  		if args.allTask {
   112  			// contradiction
   113  			return args, "either `task-name` or `all-task` should be set", false
   114  		}
   115  	case 0:
   116  		if !args.allTask {
   117  			// contradiction
   118  			return args, "either `task-name` or `all-task` should be set", false
   119  		}
   120  	default:
   121  		return args, "too many arguments are specified", false
   122  	}
   123  
   124  	return args, "", true
   125  }
   126  
   127  func startValidation(cmd *cobra.Command, _ []string) error {
   128  	args, msg, ok := parseValidationStartStopArgs(cmd, StartValidationOp)
   129  	if !ok {
   130  		return printUsageAndFailWithMessage(cmd, msg)
   131  	}
   132  	// we use taskName="" to represent we do operation on all task, so args.allTask don't need to pass
   133  	req := &pb.StartValidationRequest{
   134  		TaskName: args.taskName,
   135  		Sources:  args.sources,
   136  	}
   137  	// "validation start" has 2 usages:
   138  	// 1. if validator never started, starts it.
   139  	// 2. if validator ever started, resumes it, in this case user can't set mode or start-time explicitly.
   140  	if args.explicitMode {
   141  		req.Mode = &pb.StartValidationRequest_ModeValue{ModeValue: args.mode}
   142  	}
   143  	if args.explicitStartTime {
   144  		req.StartTime = &pb.StartValidationRequest_StartTimeValue{StartTimeValue: args.startTime}
   145  	}
   146  
   147  	ctx, cancel := context.WithCancel(context.Background())
   148  	defer cancel()
   149  	resp := &pb.StartValidationResponse{}
   150  	err := common.SendRequest(ctx, "StartValidation", req, &resp)
   151  	if err != nil {
   152  		return err
   153  	}
   154  	common.PrettyPrintResponse(resp)
   155  	return nil
   156  }
   157  
   158  func stopValidation(cmd *cobra.Command, _ []string) error {
   159  	args, msg, ok := parseValidationStartStopArgs(cmd, StopValidationOp)
   160  	if !ok {
   161  		return printUsageAndFailWithMessage(cmd, msg)
   162  	}
   163  	ctx, cancel := context.WithCancel(context.Background())
   164  	defer cancel()
   165  	resp := &pb.StopValidationResponse{}
   166  	err := common.SendRequest(
   167  		ctx,
   168  		"StopValidation",
   169  		// we use taskName="" to represent we do operation on all task, so args.allTask don't need to pass
   170  		&pb.StopValidationRequest{
   171  			TaskName: args.taskName,
   172  			Sources:  args.sources,
   173  		},
   174  		&resp,
   175  	)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	common.PrettyPrintResponse(resp)
   180  	return nil
   181  }