github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/cmd/cli/cli_changefeed_resume.go (about) 1 // Copyright 2021 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 cli 15 16 import ( 17 "context" 18 "strconv" 19 "strings" 20 21 v2 "github.com/pingcap/tiflow/cdc/api/v2" 22 apiv2client "github.com/pingcap/tiflow/pkg/api/v2" 23 cmdcontext "github.com/pingcap/tiflow/pkg/cmd/context" 24 "github.com/pingcap/tiflow/pkg/cmd/factory" 25 "github.com/pingcap/tiflow/pkg/cmd/util" 26 cerror "github.com/pingcap/tiflow/pkg/errors" 27 "github.com/spf13/cobra" 28 "github.com/tikv/client-go/v2/oracle" 29 ) 30 31 // resumeChangefeedOptions defines flags for the `cli changefeed resume` command. 32 type resumeChangefeedOptions struct { 33 apiClient apiv2client.APIV2Interface 34 35 changefeedID string 36 namespace string 37 changefeedDetail *v2.ChangeFeedInfo 38 noConfirm bool 39 overwriteCheckpointTs string 40 currentTso *v2.Tso 41 checkpointTs uint64 42 43 upstreamPDAddrs string 44 upstreamCaPath string 45 upstreamCertPath string 46 upstreamKeyPath string 47 } 48 49 // newResumeChangefeedOptions creates new options for the `cli changefeed pause` command. 50 func newResumeChangefeedOptions() *resumeChangefeedOptions { 51 return &resumeChangefeedOptions{} 52 } 53 54 // addFlags receives a *cobra.Command reference and binds 55 // flags related to template printing to it. 56 func (o *resumeChangefeedOptions) addFlags(cmd *cobra.Command) { 57 cmd.PersistentFlags().StringVarP(&o.namespace, "namespace", "n", "default", "Replication task (changefeed) Namespace") 58 cmd.PersistentFlags().StringVarP(&o.changefeedID, "changefeed-id", "c", "", "Replication task (changefeed) ID") 59 cmd.PersistentFlags().BoolVar(&o.noConfirm, "no-confirm", false, "Don't ask user whether to ignore ineligible table") 60 cmd.PersistentFlags().StringVar(&o.overwriteCheckpointTs, "overwrite-checkpoint-ts", "", 61 "Overwrite the changefeed checkpoint ts, should be 'now' or a specified tso value") 62 cmd.PersistentFlags().StringVar(&o.upstreamPDAddrs, "upstream-pd", "", 63 "upstream PD address, use ',' to separate multiple PDs") 64 cmd.PersistentFlags().StringVar(&o.upstreamCaPath, "upstream-ca", "", 65 "CA certificate path for TLS connection to upstream") 66 cmd.PersistentFlags().StringVar(&o.upstreamCertPath, "upstream-cert", "", 67 "Certificate path for TLS connection to upstream") 68 cmd.PersistentFlags().StringVar(&o.upstreamKeyPath, "upstream-key", "", 69 "Private key path for TLS connection to upstream") 70 // we don't support specify there flags below when cdc version <= 6.3.0 71 _ = cmd.PersistentFlags().MarkHidden("upstream-pd") 72 _ = cmd.PersistentFlags().MarkHidden("upstream-ca") 73 _ = cmd.PersistentFlags().MarkHidden("upstream-cert") 74 _ = cmd.PersistentFlags().MarkHidden("upstream-key") 75 76 _ = cmd.MarkPersistentFlagRequired("changefeed-id") 77 } 78 79 // complete adapts from the command line args to the data and client required. 80 func (o *resumeChangefeedOptions) complete(f factory.Factory) error { 81 apiClient, err := f.APIV2Client() 82 if err != nil { 83 return err 84 } 85 o.apiClient = apiClient 86 return nil 87 } 88 89 func (o *resumeChangefeedOptions) getUpstreamConfig() *v2.UpstreamConfig { 90 var ( 91 pdAddrs []string 92 caPath string 93 keyPath string 94 certPath string 95 ) 96 if o.upstreamPDAddrs != "" { 97 pdAddrs = strings.Split(o.upstreamPDAddrs, ",") 98 caPath = o.upstreamCaPath 99 certPath = o.upstreamCertPath 100 keyPath = o.upstreamKeyPath 101 } 102 return &v2.UpstreamConfig{ 103 PDConfig: v2.PDConfig{ 104 PDAddrs: pdAddrs, 105 CAPath: caPath, 106 CertPath: certPath, 107 KeyPath: keyPath, 108 CertAllowedCN: nil, 109 }, 110 } 111 } 112 113 func (o *resumeChangefeedOptions) getResumeChangefeedConfig() *v2.ResumeChangefeedConfig { 114 upstreamConfig := o.getUpstreamConfig() 115 return &v2.ResumeChangefeedConfig{ 116 OverwriteCheckpointTs: o.checkpointTs, 117 PDConfig: upstreamConfig.PDConfig, 118 } 119 } 120 121 func (o *resumeChangefeedOptions) getTSO(ctx context.Context) (*v2.Tso, error) { 122 tso, err := o.apiClient.Tso().Query(ctx, 123 &v2.UpstreamConfig{ID: o.changefeedDetail.UpstreamID}) 124 if err != nil { 125 return nil, err 126 } 127 128 return tso, nil 129 } 130 131 func (o *resumeChangefeedOptions) getChangefeedInfo(ctx context.Context) ( 132 *v2.ChangeFeedInfo, error, 133 ) { 134 detail, err := o.apiClient.Changefeeds().Get(ctx, o.namespace, o.changefeedID) 135 if err != nil { 136 return nil, err 137 } 138 139 return detail, nil 140 } 141 142 // confirmResumeChangefeedCheck prompts the user to confirm the use of a large data gap when noConfirm is turned off. 143 func (o *resumeChangefeedOptions) confirmResumeChangefeedCheck(cmd *cobra.Command) error { 144 if !o.noConfirm { 145 if len(o.overwriteCheckpointTs) == 0 { 146 return confirmLargeDataGap(cmd, o.currentTso.Timestamp, 147 o.changefeedDetail.CheckpointTs, "resume") 148 } 149 150 return confirmOverwriteCheckpointTs(cmd, o.changefeedID, o.checkpointTs) 151 } 152 return nil 153 } 154 155 func (o *resumeChangefeedOptions) validateParams(ctx context.Context) error { 156 // check whether the changefeed to be resumed is existing 157 detail, err := o.getChangefeedInfo(ctx) 158 if err != nil { 159 return err 160 } 161 o.changefeedDetail = detail 162 163 tso, err := o.getTSO(ctx) 164 if err != nil { 165 return err 166 } 167 o.currentTso = tso 168 169 if len(o.overwriteCheckpointTs) == 0 { 170 return nil 171 } 172 173 // validate the --overwrite-checkpoint-ts parameter 174 if strings.ToLower(o.overwriteCheckpointTs) == "now" { 175 o.checkpointTs = oracle.ComposeTS(tso.Timestamp, tso.LogicTime) 176 return nil 177 } 178 179 checkpointTs, err := strconv.ParseUint(o.overwriteCheckpointTs, 10, 64) 180 if err != nil { 181 return cerror.ErrCliInvalidCheckpointTs.GenWithStackByArgs(o.overwriteCheckpointTs) 182 } 183 184 if checkpointTs == 0 { 185 return cerror.ErrCliInvalidCheckpointTs.GenWithStackByArgs(o.overwriteCheckpointTs) 186 } 187 188 if checkpointTs > oracle.ComposeTS(tso.Timestamp, tso.LogicTime) { 189 return cerror.ErrCliCheckpointTsIsInFuture.GenWithStackByArgs(checkpointTs) 190 } 191 192 o.checkpointTs = checkpointTs 193 return nil 194 } 195 196 // run the `cli changefeed resume` command. 197 func (o *resumeChangefeedOptions) run(cmd *cobra.Command) error { 198 ctx := cmdcontext.GetDefaultContext() 199 200 if err := o.validateParams(ctx); err != nil { 201 return err 202 } 203 204 cfg := o.getResumeChangefeedConfig() 205 if err := o.confirmResumeChangefeedCheck(cmd); err != nil { 206 return err 207 } 208 err := o.apiClient.Changefeeds().Resume(ctx, cfg, o.namespace, o.changefeedID) 209 210 return err 211 } 212 213 // newCmdResumeChangefeed creates the `cli changefeed resume` command. 214 func newCmdResumeChangefeed(f factory.Factory) *cobra.Command { 215 o := newResumeChangefeedOptions() 216 217 command := &cobra.Command{ 218 Use: "resume", 219 Short: "Resume a paused replication task (changefeed)", 220 Args: cobra.NoArgs, 221 Run: func(cmd *cobra.Command, args []string) { 222 util.CheckErr(o.complete(f)) 223 util.CheckErr(o.run(cmd)) 224 }, 225 } 226 227 o.addFlags(command) 228 229 return command 230 }