github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cmd/cmd_test.go (about)

     1  // Copyright 2020 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 cmd
    15  
    16  import (
    17  	"context"
    18  	"io/ioutil"
    19  	"os"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/pingcap/check"
    24  	"github.com/pingcap/parser/model"
    25  	"github.com/pingcap/ticdc/pkg/config"
    26  	"github.com/pingcap/ticdc/pkg/util/testleak"
    27  	"github.com/pingcap/tidb/store/tikv/oracle"
    28  	"github.com/spf13/cobra"
    29  	pd "github.com/tikv/pd/client"
    30  )
    31  
    32  func TestSuite(t *testing.T) { check.TestingT(t) }
    33  
    34  type decodeFileSuite struct{}
    35  
    36  var _ = check.Suite(&decodeFileSuite{})
    37  
    38  func (s *decodeFileSuite) TestCanDecodeTOML(c *check.C) {
    39  	defer testleak.AfterTest(c)()
    40  	dir := c.MkDir()
    41  	path := filepath.Join(dir, "config.toml")
    42  	content := `
    43  case-sensitive = false
    44  
    45  [filter]
    46  ignore-txn-start-ts = [1, 2]
    47  ddl-allow-list = [1, 2]
    48  rules = ['*.*', '!test.*']
    49  
    50  [mounter]
    51  worker-num = 64
    52  
    53  [sink]
    54  dispatchers = [
    55  	{matcher = ['test1.*', 'test2.*'], dispatcher = "ts"},
    56  	{matcher = ['test3.*', 'test4.*'], dispatcher = "rowid"},
    57  ]
    58  protocol = "default"
    59  
    60  [cyclic-replication]
    61  enable = true
    62  replica-id = 1
    63  filter-replica-ids = [2,3]
    64  id-buckets = 4
    65  sync-ddl = true
    66  
    67  [scheduler]
    68  type = "manual"
    69  polling-time = 5
    70  `
    71  	err := ioutil.WriteFile(path, []byte(content), 0o644)
    72  	c.Assert(err, check.IsNil)
    73  
    74  	cfg := config.GetDefaultReplicaConfig()
    75  	err = strictDecodeFile(path, "cdc", &cfg)
    76  	c.Assert(err, check.IsNil)
    77  
    78  	c.Assert(cfg.CaseSensitive, check.IsFalse)
    79  	c.Assert(cfg.Filter, check.DeepEquals, &config.FilterConfig{
    80  		IgnoreTxnStartTs: []uint64{1, 2},
    81  		DDLAllowlist:     []model.ActionType{1, 2},
    82  		Rules:            []string{"*.*", "!test.*"},
    83  	})
    84  	c.Assert(cfg.Mounter, check.DeepEquals, &config.MounterConfig{
    85  		WorkerNum: 64,
    86  	})
    87  	c.Assert(cfg.Sink, check.DeepEquals, &config.SinkConfig{
    88  		DispatchRules: []*config.DispatchRule{
    89  			{Dispatcher: "ts", Matcher: []string{"test1.*", "test2.*"}},
    90  			{Dispatcher: "rowid", Matcher: []string{"test3.*", "test4.*"}},
    91  		},
    92  		Protocol: "default",
    93  	})
    94  	c.Assert(cfg.Cyclic, check.DeepEquals, &config.CyclicConfig{
    95  		Enable:          true,
    96  		ReplicaID:       1,
    97  		FilterReplicaID: []uint64{2, 3},
    98  		IDBuckets:       4,
    99  		SyncDDL:         true,
   100  	})
   101  	c.Assert(cfg.Scheduler, check.DeepEquals, &config.SchedulerConfig{
   102  		Tp:          "manual",
   103  		PollingTime: 5,
   104  	})
   105  }
   106  
   107  func (s *decodeFileSuite) TestAndWriteExampleReplicaTOML(c *check.C) {
   108  	defer testleak.AfterTest(c)()
   109  	cfg := config.GetDefaultReplicaConfig()
   110  	err := strictDecodeFile("changefeed.toml", "cdc", &cfg)
   111  	c.Assert(err, check.IsNil)
   112  
   113  	c.Assert(cfg.CaseSensitive, check.IsTrue)
   114  	c.Assert(cfg.Filter, check.DeepEquals, &config.FilterConfig{
   115  		IgnoreTxnStartTs: []uint64{1, 2},
   116  		Rules:            []string{"*.*", "!test.*"},
   117  	})
   118  	c.Assert(cfg.Mounter, check.DeepEquals, &config.MounterConfig{
   119  		WorkerNum: 16,
   120  	})
   121  	c.Assert(cfg.Sink, check.DeepEquals, &config.SinkConfig{
   122  		DispatchRules: []*config.DispatchRule{
   123  			{Dispatcher: "ts", Matcher: []string{"test1.*", "test2.*"}},
   124  			{Dispatcher: "rowid", Matcher: []string{"test3.*", "test4.*"}},
   125  		},
   126  		Protocol: "default",
   127  	})
   128  	c.Assert(cfg.Cyclic, check.DeepEquals, &config.CyclicConfig{
   129  		Enable:          false,
   130  		ReplicaID:       1,
   131  		FilterReplicaID: []uint64{2, 3},
   132  		SyncDDL:         true,
   133  	})
   134  }
   135  
   136  func (s *decodeFileSuite) TestAndWriteExampleServerTOML(c *check.C) {
   137  	defer testleak.AfterTest(c)()
   138  	cfg := config.GetDefaultServerConfig()
   139  	err := strictDecodeFile("ticdc.toml", "cdc", &cfg)
   140  	c.Assert(err, check.IsNil)
   141  	defcfg := config.GetDefaultServerConfig()
   142  	defcfg.AdvertiseAddr = "127.0.0.1:8300"
   143  	defcfg.LogFile = "/tmp/ticdc/ticdc.log"
   144  	c.Assert(cfg, check.DeepEquals, defcfg)
   145  }
   146  
   147  func (s *decodeFileSuite) TestShouldReturnErrForUnknownCfgs(c *check.C) {
   148  	defer testleak.AfterTest(c)()
   149  	dir := c.MkDir()
   150  	path := filepath.Join(dir, "config.toml")
   151  	content := `filter-case-insensitive = true`
   152  	err := ioutil.WriteFile(path, []byte(content), 0o644)
   153  	c.Assert(err, check.IsNil)
   154  
   155  	cfg := config.GetDefaultReplicaConfig()
   156  	err = strictDecodeFile(path, "cdc", &cfg)
   157  	c.Assert(err, check.NotNil)
   158  	c.Assert(err, check.ErrorMatches, ".*unknown config.*")
   159  }
   160  
   161  func (s *decodeFileSuite) TestVerifyReplicaConfig(c *check.C) {
   162  	defer testleak.AfterTest(c)()
   163  
   164  	dir := c.MkDir()
   165  	path := filepath.Join(dir, "config.toml")
   166  	content := `
   167  	[filter]
   168  	rules = ['*.*', '!test.*']`
   169  	err := ioutil.WriteFile(path, []byte(content), 0o644)
   170  	c.Assert(err, check.IsNil)
   171  
   172  	cfg := config.GetDefaultReplicaConfig()
   173  	err = verifyReplicaConfig(path, "cdc", cfg)
   174  	c.Assert(err, check.IsNil)
   175  
   176  	path = filepath.Join(dir, "config1.toml")
   177  	content = `
   178  	[filter]
   179  	rules = ['*.*', '!test.*','rtest1']`
   180  	err = ioutil.WriteFile(path, []byte(content), 0o644)
   181  	c.Assert(err, check.IsNil)
   182  
   183  	cfg = config.GetDefaultReplicaConfig()
   184  	err = verifyReplicaConfig(path, "cdc", cfg)
   185  	c.Assert(err, check.NotNil)
   186  	c.Assert(err, check.ErrorMatches, ".*CDC:ErrFilterRuleInvalid.*")
   187  }
   188  
   189  type mockPDClient struct {
   190  	pd.Client
   191  	ts uint64
   192  }
   193  
   194  func (m *mockPDClient) GetTS(ctx context.Context) (int64, int64, error) {
   195  	return oracle.ExtractPhysical(m.ts), 0, nil
   196  }
   197  
   198  type commonUtilSuite struct{}
   199  
   200  var _ = check.Suite(&commonUtilSuite{})
   201  
   202  func (s *commonUtilSuite) TestConfirmLargeDataGap(c *check.C) {
   203  	defer testleak.AfterTest(c)()
   204  	ctx := context.Background()
   205  	currentTs := uint64(423482306736160769) // 2021-03-11 17:59:57.547
   206  	startTs := uint64(423450030227042420)   // 2021-03-10 07:47:52.435
   207  	pdCli = &mockPDClient{ts: currentTs}
   208  	cmd := &cobra.Command{}
   209  
   210  	// check start ts more than 1 day before current ts, and type N when confirming
   211  	dir := c.MkDir()
   212  	path := filepath.Join(dir, "confirm.txt")
   213  	err := ioutil.WriteFile(path, []byte("n"), 0o644)
   214  	c.Assert(err, check.IsNil)
   215  	f, err := os.Open(path)
   216  	c.Assert(err, check.IsNil)
   217  	stdin := os.Stdin
   218  	os.Stdin = f
   219  	defer func() {
   220  		os.Stdin = stdin
   221  	}()
   222  	err = confirmLargeDataGap(ctx, cmd, startTs)
   223  	c.Assert(err, check.ErrorMatches, "abort changefeed create or resume")
   224  
   225  	// check no confirm works
   226  	originNoConfirm := noConfirm
   227  	noConfirm = true
   228  	defer func() {
   229  		noConfirm = originNoConfirm
   230  	}()
   231  	err = confirmLargeDataGap(ctx, cmd, startTs)
   232  	c.Assert(err, check.IsNil)
   233  	noConfirm = false
   234  
   235  	// check start ts more than 1 day before current ts, and type Y when confirming
   236  	err = ioutil.WriteFile(path, []byte("Y"), 0o644)
   237  	c.Assert(err, check.IsNil)
   238  	f, err = os.Open(path)
   239  	c.Assert(err, check.IsNil)
   240  	os.Stdin = f
   241  	err = confirmLargeDataGap(ctx, cmd, startTs)
   242  	c.Assert(err, check.IsNil)
   243  
   244  	// check start ts does not exceed threshold
   245  	pdCli = &mockPDClient{ts: startTs}
   246  	err = confirmLargeDataGap(ctx, cmd, startTs)
   247  	c.Assert(err, check.IsNil)
   248  }