Repository: pingcap/tiflow (go)

Issue:
dm failed to run when only `ssl-ca` is configured
### What did you do?

- target db is tidbcloud tidb cluster
- use ssl to connect target db, tidbcloud only has `ca` part
- start-task, and fail with `"Message": "[Lightning:DB:ErrDBConnect]failed to connect database: could not load client key pair: tls: failed to find any PEM data in certificate input",`

bug was introduced in https://github.com/pingcap/tiflow/pull/3260, if user didn't specify some `ssl-xxx`(like in here, there's only `ssl-ca` config, no `ssl-cert` and `ssl-key`), we should not dump it.
https://github.com/pingcap/tiflow/blob/45ee743ccbea727a3c5d360a1295e51d61acd1c4/dm/config/security/security.go#L105-L109

task cfg
```
name: test
task-mode: all

target-database:
  host: "xxxxxx.clusters.dev.tidb-cloud.com"
  port: 4000
  user: "root"
  password: "xxx"
  security:
    ssl-ca: "tidbcloud-ca.pem"

mysql-instances:
  - source-id: "mysql-3306"
    block-allow-list: "ba-rule1"
    syncer-config-name: "global"

block-allow-list:
  ba-rule1:
    do-dbs: ["test"]

syncers:
  global:
    worker-count: 1
    batch: 1
```

### What did you expect to see?

success

### What did you see instead?

fail

### Versions of the cluster

tested using 6.1.3, ~~but is should fail on current master too~~, master branch change lightning tls config to `sslCABytes`, so no such problem

### current status of DM cluster (execute `query-status <task-name>` in dmctl)

```console
(paste current status of DM cluster here)
```

Pull request description:
tls(dm): fix the error when only ssl-ca is configured
<!--
Thank you for contributing to TiFlow! 
Please read MD's [CONTRIBUTING](https://github.com/pingcap/tiflow/blob/master/CONTRIBUTING.md) document **BEFORE** filing this PR.
-->

### What problem does this PR solve?
<!--
Please create an issue first to describe the problem.

There MUST be one line starting with "Issue Number:  " and 
linking the relevant issues via the "close" or "ref".

For more info, check https://pingcap.github.io/tidb-dev-guide/contribute-to-tidb/contribute-code.html#referring-to-an-issue.
 -->

Issue Number: close #7941

### What is changed and how it works?
don't dump if user didn't specify some `ssl-xxx`

### Check List <!--REMOVE the items that are not applicable-->

#### Tests <!-- At least one of them must be included. -->

 - Unit test
 - Integration test


#### Questions <!-- Authors should answer these questions and reviewers should consider these questions. -->

##### Will it cause performance regression or break compatibility?
No
##### Do you need to update user documentation, design documentation or monitoring documentation?
No
### Release note <!-- bugfixes or new features need a release note -->

```release-note
None
```


Patch:
diff --git a/dm/dm/config/security.go b/dm/dm/config/security.go
index 99c05f5bb..8fc10624f 100644
--- a/dm/dm/config/security.go
+++ b/dm/dm/config/security.go
@@ -90,19 +90,19 @@ func (s *Security) DumpTLSContent(baseDirPath string) error {
 			}
 		}
 	}
-	if isSSLCANotExist {
+	if isSSLCANotExist && len(s.SSLCABytes) > 0 {
 		s.SSLCA = path.Join(baseDirPath, "ca.pem")
 		if err := utils.WriteFileAtomic(s.SSLCA, s.SSLCABytes, 0o600); err != nil {
 			return err
 		}
 	}
-	if isSSLCertNotExist {
+	if isSSLCertNotExist && len(s.SSLCertBytes) > 0 {
 		s.SSLCert = path.Join(baseDirPath, "cert.pem")
 		if err := utils.WriteFileAtomic(s.SSLCert, s.SSLCertBytes, 0o600); err != nil {
 			return err
 		}
 	}
-	if isSSLKeyNotExist {
+	if isSSLKeyNotExist && len(s.SSLKEYBytes) > 0 {
 		s.SSLKey = path.Join(baseDirPath, "key.pem")
 		if err := utils.WriteFileAtomic(s.SSLKey, s.SSLKEYBytes, 0o600); err != nil {
 			return err


Test patch:
diff --git a/dm/dm/config/security_test.go b/dm/dm/config/security_test.go
index 0fd85a669..6cce12769 100644
--- a/dm/dm/config/security_test.go
+++ b/dm/dm/config/security_test.go
@@ -187,4 +187,18 @@ func (t *testTLSConfig) TestLoadDumpTLSContent(c *C) {
 	c.Assert(utils.IsFileExists(s.SSLCA), Equals, true)
 	c.Assert(utils.IsFileExists(s.SSLCert), Equals, true)
 	c.Assert(utils.IsFileExists(s.SSLKey), Equals, true)
+
+	// test only ssl-ca config
+	s = &Security{
+		SSLCA: caFilePath,
+	}
+	err = s.LoadTLSContent()
+	c.Assert(err, IsNil)
+	c.Assert(s.DumpTLSContent(c.MkDir()), IsNil)
+	c.Assert(len(s.SSLCABytes) > 0, Equals, true)
+	c.Assert(len(s.SSLCertBytes) > 0, Equals, false)
+	c.Assert(len(s.SSLKEYBytes) > 0, Equals, false)
+	c.Assert(utils.IsFileExists(s.SSLCA), Equals, true)
+	c.Assert(utils.IsFileExists(s.SSLCert), Equals, false)
+	c.Assert(utils.IsFileExists(s.SSLKey), Equals, false)
 }
diff --git a/dm/tests/tls/conf/dm-master-no-tls.toml b/dm/tests/tls/conf/dm-master-no-tls.toml
new file mode 100644
index 000000000..7cecf59ad
--- /dev/null
+++ b/dm/tests/tls/conf/dm-master-no-tls.toml
@@ -0,0 +1,4 @@
+# Master Configuration.
+master-addr = ":8261"
+advertise-addr = "127.0.0.1:8261"
+auto-compaction-retention = "3s"
diff --git a/dm/tests/tls/conf/dm-task-only-ca.yaml b/dm/tests/tls/conf/dm-task-only-ca.yaml
new file mode 100644
index 000000000..c01fe121f
--- /dev/null
+++ b/dm/tests/tls/conf/dm-task-only-ca.yaml
@@ -0,0 +1,42 @@
+---
+name: test
+task-mode: all
+is-sharding: false
+meta-schema: "dm_meta"
+
+target-database:
+  host: "127.0.0.1"
+  port: 4400
+  user: "root"
+  password: ""
+  security:
+    ssl-ca: "dir-placeholer/task-ca.pem"
+
+
+mysql-instances:
+  - source-id: "mysql-replica-01"
+    black-white-list: "instance"
+    mydumper-config-name: "global"
+    loader-config-name: "global"
+    syncer-config-name: "global"
+
+black-white-list:
+  instance:
+    do-dbs: ["tls"]
+
+mydumpers:
+  global:
+    threads: 4
+    chunk-filesize: 0
+    skip-tz-utc: true
+    extra-args: "--statement-size=100"
+
+loaders:
+  global:
+    pool-size: 16
+    dir: "./dumped_data"
+
+syncers:
+  global:
+    worker-count: 16
+    batch: 100
diff --git a/dm/tests/tls/conf/dm-worker-no-tls.toml b/dm/tests/tls/conf/dm-worker-no-tls.toml
new file mode 100644
index 000000000..ab7e1b9cb
--- /dev/null
+++ b/dm/tests/tls/conf/dm-worker-no-tls.toml
@@ -0,0 +1,2 @@
+name = "worker3"
+join = "127.0.0.1:8261"
diff --git a/dm/tests/tls/conf/source-no-tls.yaml b/dm/tests/tls/conf/source-no-tls.yaml
new file mode 100644
index 000000000..fae9f5bd7
--- /dev/null
+++ b/dm/tests/tls/conf/source-no-tls.yaml
@@ -0,0 +1,11 @@
+source-id: mysql-replica-01
+flavor: ''
+enable-gtid: true
+enable-relay: true
+relay-binlog-name: ''
+relay-binlog-gtid: ''
+from:
+  host: 127.0.0.1
+  user: root
+  password: /Q7B9DizNLLTTfiZHv9WoEAKamfpIUs=
+  port: 3306
diff --git a/dm/tests/tls/run.sh b/dm/tests/tls/run.sh
index 962dd90c1..2bb57dbe5 100644
--- a/dm/tests/tls/run.sh
+++ b/dm/tests/tls/run.sh
@@ -384,12 +384,58 @@ function test_master_ha_when_enable_tidb_and_only_ca_source_tls() {
 	echo "============================== test_master_ha_when_enable_tidb_and_only_ca_source_tls success =================================="
 }
 
+function test_source_and_target_when_only_ca_task() {
+	prepare_test
+
+	cp $cur/conf/source-no-tls.yaml $WORK_DIR/
+	cp $cur/conf/dm-master-no-tls.toml $WORK_DIR/
+	cp $cur/conf/dm-worker-no-tls.toml $WORK_DIR/
+	cp $cur/conf/dm-task-only-ca.yaml $WORK_DIR/
+
+	sed -i "s%dir-placeholer%$cur\/conf%g" $WORK_DIR/dm-task-only-ca.yaml
+
+	# start DM worker and master
+	run_dm_master $WORK_DIR/master $MASTER_PORT $WORK_DIR/dm-master-no-tls.toml
+	check_rpc_alive $cur/../bin/check_master_online 127.0.0.1:$MASTER_PORT
+	run_dm_worker $WORK_DIR/worker3 $WORKER3_PORT $WORK_DIR/dm-worker-no-tls.toml
+	check_rpc_alive $cur/../bin/check_worker_online 127.0.0.1:$WORKER3_PORT
+
+	# operate mysql config to worker
+	run_dm_ctl_with_retry $WORK_DIR "127.0.0.1:$MASTER_PORT" \
+		"operate-source create $WORK_DIR/source-no-tls.yaml" \
+		"\"result\": true" 2 \
+		"\"source\": \"$SOURCE_ID1\"" 1
+
+	echo "check master alive"
+	run_dm_ctl_with_retry $WORK_DIR "127.0.0.1:$MASTER_PORT" \
+		"list-member" \
+		"\"alive\": true" 1
+
+	echo "start task and check stage"
+	run_dm_ctl_with_retry $WORK_DIR "127.0.0.1:$MASTER_PORT" \
+		"start-task $WORK_DIR/dm-task-only-ca.yaml --remove-meta=true" \
+		"\"result\": true" 2
+
+	run_dm_ctl_with_retry $WORK_DIR "127.0.0.1:$MASTER_PORT" \
+		"query-status test" \
+		"\"result\": true" 2 \
+		"\"unit\": \"Sync\"" 1
+
+	run_sql 'INSERT INTO tls.t VALUES (99,9999999);' $MYSQL_PORT1 $MYSQL_PASSWORD1
+
+	echo "check data"
+	check_sync_diff $WORK_DIR $cur/conf/diff_config.toml
+
+	echo "============================== test_source_and_target_when_only_ca_task success =================================="
+}
+
 function run() {
 	test_master_ha_when_enable_tidb_and_only_ca_source_tls
 
 	test_worker_handle_multi_tls_tasks
 	test_worker_download_certs_from_master
 	test_worker_ha_when_enable_source_tls
+	test_source_and_target_when_only_ca_task
 }
 
 cleanup_data tls