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 }