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  }