You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by mi...@apache.org on 2023/04/27 06:24:50 UTC

[shardingsphere-on-cloud] branch main updated: feat(pitr): try to cleanup backup data if exec backup failed.

This is an automated email from the ASF dual-hosted git repository.

miaoliyao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shardingsphere-on-cloud.git


The following commit(s) were added to refs/heads/main by this push:
     new 095d94c  feat(pitr): try to cleanup backup data if exec backup failed.
     new ace6bf6  Merge pull request #329 from Xu-Wentao/pitr
095d94c is described below

commit 095d94c89787d4f070e4c6a76148329b1270551d
Author: xuwentao <cu...@yahoo.com>
AuthorDate: Wed Apr 26 18:50:02 2023 +0800

    feat(pitr): try to cleanup backup data if exec backup failed.
---
 pitr/agent/go.mod                                 |  11 +-
 pitr/agent/go.sum                                 |  28 ++-
 pitr/agent/internal/cons/error.go                 |   3 +-
 pitr/agent/internal/handler/backup.go             |  22 ++
 pitr/agent/internal/handler/backup_test.go        |  76 +++++++
 pitr/agent/internal/handler/diskspace_test.go     |   2 -
 pitr/agent/internal/handler/handler_suite_test.go |  15 +-
 pitr/agent/internal/handler/view/backup.go        |  51 ++++-
 pitr/agent/internal/pkg/mocks/opengauss.go        | 249 ++++++++++++++++++++++
 pitr/agent/internal/pkg/opengauss.go              |   6 +-
 pitr/agent/internal/pkg/opengauss_test.go         |   4 +-
 pitr/agent/main.go                                |   1 +
 pitr/cli/internal/cmd/backup.go                   | 100 ++++++++-
 pitr/cli/internal/cmd/backup_test.go              |  41 +++-
 pitr/cli/internal/cmd/restore_test.go             |   1 +
 pitr/cli/internal/pkg/agent-server.go             |  32 +++
 pitr/cli/internal/pkg/mocks/agent-server.go       |  31 ++-
 pitr/cli/internal/pkg/model/as_backup.go          |  20 +-
 18 files changed, 642 insertions(+), 51 deletions(-)

diff --git a/pitr/agent/go.mod b/pitr/agent/go.mod
index 82bfb84..3fa2ddd 100644
--- a/pitr/agent/go.mod
+++ b/pitr/agent/go.mod
@@ -6,6 +6,7 @@ require (
 	gitee.com/opengauss/openGauss-connector-go-pq v1.0.4
 	github.com/dlclark/regexp2 v1.8.0
 	github.com/gofiber/fiber/v2 v2.42.0
+	github.com/golang/mock v1.6.0
 	github.com/joho/godotenv v1.5.1
 	github.com/onsi/ginkgo/v2 v2.8.0
 	github.com/onsi/gomega v1.26.0
@@ -28,7 +29,7 @@ require (
 	github.com/rogpeppe/go-internal v1.8.0 // indirect
 	github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
 	github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
-	github.com/stretchr/testify v1.8.1 // indirect
+	github.com/stretchr/testify v1.8.2 // indirect
 	github.com/tinylib/msgp v1.1.6 // indirect
 	github.com/tjfoc/gmsm v1.4.1 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
@@ -36,10 +37,10 @@ require (
 	github.com/valyala/tcplisten v1.0.0 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/multierr v1.8.0 // indirect
-	golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
-	golang.org/x/net v0.5.0 // indirect
-	golang.org/x/sys v0.4.0 // indirect
-	golang.org/x/text v0.6.0 // indirect
+	golang.org/x/crypto v0.7.0 // indirect
+	golang.org/x/net v0.8.0 // indirect
+	golang.org/x/sys v0.6.0 // indirect
+	golang.org/x/text v0.8.0 // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
diff --git a/pitr/agent/go.sum b/pitr/agent/go.sum
index 35a3b4c..ce24fbf 100644
--- a/pitr/agent/go.sum
+++ b/pitr/agent/go.sum
@@ -23,6 +23,8 @@ github.com/gofiber/fiber/v2 v2.42.0 h1:Fnp7ybWvS+sjNQsFvkhf4G8OhXswvB6Vee8hM/LyS
 github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
@@ -90,8 +92,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
 github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
 github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
@@ -103,6 +105,7 @@ github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seB
 github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
 github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
 go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -116,13 +119,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
 golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
+golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -132,34 +137,38 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
-golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
+golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
+golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
+golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -167,6 +176,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pitr/agent/internal/cons/error.go b/pitr/agent/internal/cons/error.go
index dcb875b..761fe6f 100644
--- a/pitr/agent/internal/cons/error.go
+++ b/pitr/agent/internal/cons/error.go
@@ -33,7 +33,7 @@ var (
 	StartOpenGaussFailed   = xerror.New(10008, "Failed to start opengauss.")
 	StopOpenGaussFailed    = xerror.New(10009, "Failed to stop opengauss.")
 	RestoreFailed          = xerror.New(10010, "Failed to restore opengauss.")
-	InvalidDBPort          = xerror.New(10011, "Invalid dn port.")
+	InvalidDBPort          = xerror.New(10011, "Invalid db port or missing db port.")
 	MissingUsername        = xerror.New(10012, "Missing username")
 	MissingPassword        = xerror.New(10013, "Missing password.")
 	MissingDnBackupPath    = xerror.New(10014, "Missing dn backup path.")
@@ -52,4 +52,5 @@ var (
 	MvTempToPgDataFailed   = xerror.New(10027, "Move temp dir to pgdata failed.")
 	CleanPgDataTempFailed  = xerror.New(10028, "Clean pgdata temp dir failed.")
 	MissingDiskPath        = xerror.New(10029, "Missing disk path.")
+	MissingBackupID        = xerror.New(10030, "Missing backup id.")
 )
diff --git a/pitr/agent/internal/handler/backup.go b/pitr/agent/internal/handler/backup.go
index bb000f7..4135e49 100644
--- a/pitr/agent/internal/handler/backup.go
+++ b/pitr/agent/internal/handler/backup.go
@@ -59,3 +59,25 @@ func Backup(ctx *fiber.Ctx) error {
 		ID: backupID,
 	})
 }
+
+func DeleteBackup(ctx *fiber.Ctx) error {
+	in := &view.DeleteBackupIn{}
+	if err := ctx.BodyParser(in); err != nil {
+		return fmt.Errorf("body parse err=%s,wrap=%w", err, cons.BodyParseFailed)
+	}
+
+	if err := in.Validate(); err != nil {
+		return fmt.Errorf("invalid parameter,err=%w", err)
+	}
+
+	if err := pkg.OG.Auth(in.Username, in.Password, in.DBName, in.DBPort); err != nil {
+		efmt := "pkg.OG.Auth failure[un=%s,pw.len=%d,db=%s],err=%w"
+		return fmt.Errorf(efmt, in.Username, len(in.Password), in.DBName, err)
+	}
+
+	if err := pkg.OG.DelBackup(in.DnBackupPath, in.Instance, in.BackupID); err != nil {
+		return fmt.Errorf("delete backup failure,err=%w", err)
+	}
+
+	return responder.Success(ctx, "")
+}
diff --git a/pitr/agent/internal/handler/backup_test.go b/pitr/agent/internal/handler/backup_test.go
new file mode 100644
index 0000000..4f4ed51
--- /dev/null
+++ b/pitr/agent/internal/handler/backup_test.go
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package handler_test
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"strings"
+
+	"github.com/apache/shardingsphere-on-cloud/pitr/agent/internal/pkg"
+	mock_pkg "github.com/apache/shardingsphere-on-cloud/pitr/agent/internal/pkg/mocks"
+	"github.com/golang/mock/gomock"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("Backup", func() {
+	Context("delete backup", func() {
+		var mockOG *mock_pkg.MockIOpenGauss
+		BeforeEach(func() {
+			ctrl = gomock.NewController(GinkgoT())
+			mockOG = mock_pkg.NewMockIOpenGauss(ctrl)
+			pkg.OG = mockOG
+		})
+		AfterEach(func() {
+			ctrl.Finish()
+		})
+
+		It("delete failed with lack of required params", func() {
+			requestBody := `{"db_port":3306,"db_name":"test_db"}`
+			req := httptest.NewRequest(http.MethodDelete, "/api/backup", strings.NewReader(requestBody))
+			req.Header.Set("Content-Type", "application/json")
+
+			resp, err := app.Test(req)
+			Expect(err).To(BeNil())
+			Expect(resp.StatusCode).To(Equal(500))
+		})
+
+		It("delete success", func() {
+			requestBody := `{
+				"db_port": 3306,
+				"db_name": "test_db",
+				"username": "user",
+				"password": "password",
+				"dn_backup_path": "/tmp",
+				"backup_id": "backup_id",
+				"instance": "instance"
+			}`
+
+			mockOG.EXPECT().Auth(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+			mockOG.EXPECT().DelBackup(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+
+			req := httptest.NewRequest(http.MethodDelete, "/api/backup", strings.NewReader(requestBody))
+			req.Header.Set("Content-Type", "application/json")
+
+			resp, err := app.Test(req)
+			Expect(err).To(BeNil())
+			Expect(resp.StatusCode).To(Equal(http.StatusOK))
+		})
+	})
+})
diff --git a/pitr/agent/internal/handler/diskspace_test.go b/pitr/agent/internal/handler/diskspace_test.go
index 723cca0..eda49a8 100644
--- a/pitr/agent/internal/handler/diskspace_test.go
+++ b/pitr/agent/internal/handler/diskspace_test.go
@@ -20,7 +20,6 @@ package handler_test
 import (
 	"net/http"
 	"net/http/httptest"
-	"os"
 	"strings"
 
 	. "github.com/onsi/ginkgo/v2"
@@ -37,7 +36,6 @@ var _ = Describe("Disk Space", func() {
 	})
 
 	It("DiskSpace", func() {
-		Expect(os.Setenv("SHELL", "/bin/bash")).To(Succeed())
 		requestBody := `{"diskPath": "/tmp"}`
 		req := httptest.NewRequest(http.MethodPost, "/api/diskspace", strings.NewReader(requestBody))
 		req.Header.Set("Content-Type", "application/json")
diff --git a/pitr/agent/internal/handler/handler_suite_test.go b/pitr/agent/internal/handler/handler_suite_test.go
index 4984b3f..816ef69 100644
--- a/pitr/agent/internal/handler/handler_suite_test.go
+++ b/pitr/agent/internal/handler/handler_suite_test.go
@@ -18,18 +18,23 @@
 package handler_test
 
 import (
+	"os"
 	"testing"
 
 	"github.com/apache/shardingsphere-on-cloud/pitr/agent/internal/handler"
 	"github.com/apache/shardingsphere-on-cloud/pitr/agent/pkg/logging"
 	"github.com/apache/shardingsphere-on-cloud/pitr/agent/pkg/responder"
 	"github.com/gofiber/fiber/v2"
+	"github.com/golang/mock/gomock"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 	"go.uber.org/zap"
 )
 
-var app *fiber.App
+var (
+	app  *fiber.App
+	ctrl *gomock.Controller
+)
 
 func TestHandler(t *testing.T) {
 	RegisterFailHandler(Fail)
@@ -40,16 +45,16 @@ var _ = BeforeSuite(func() {
 	// init log
 	logging.Init(zap.DebugLevel)
 
+	Expect(os.Setenv("SHELL", "/bin/bash")).To(Succeed())
+
 	// init app
 	app = fiber.New()
 	app.Get("/ping", func(ctx *fiber.Ctx) error {
 		return responder.Success(ctx, "pong")
 	})
+
 	app.Route("/api", func(r fiber.Router) {
 		r.Post("/diskspace", handler.DiskSpace)
+		r.Delete("/backup", handler.DeleteBackup)
 	})
 })
-
-var _ = AfterSuite(func() {
-	_ = app.Shutdown()
-})
diff --git a/pitr/agent/internal/handler/view/backup.go b/pitr/agent/internal/handler/view/backup.go
index 738078b..6b38b6b 100644
--- a/pitr/agent/internal/handler/view/backup.go
+++ b/pitr/agent/internal/handler/view/backup.go
@@ -17,7 +17,9 @@
 
 package view
 
-import "github.com/apache/shardingsphere-on-cloud/pitr/agent/internal/cons"
+import (
+	"github.com/apache/shardingsphere-on-cloud/pitr/agent/internal/cons"
+)
 
 type (
 	BackupIn struct {
@@ -35,6 +37,17 @@ type (
 	BackupOut struct {
 		ID string `json:"backup_id"`
 	}
+
+	DeleteBackupIn struct {
+		DBPort   uint16 `json:"db_port"`
+		DBName   string `json:"db_name"`
+		Username string `json:"username"`
+		Password string `json:"password"`
+
+		DnBackupPath string `json:"dn_backup_path"`
+		Instance     string `json:"instance"`
+		BackupID     string `json:"backup_id"`
+	}
 )
 
 func (in *BackupIn) Validate() error {
@@ -79,3 +92,39 @@ func (in *BackupIn) Validate() error {
 	}
 	return nil
 }
+
+// nolint:dupl
+func (in *DeleteBackupIn) Validate() error {
+	if in == nil {
+		return cons.Internal
+	}
+
+	if in.DBPort == 0 {
+		return cons.InvalidDBPort
+	}
+
+	if in.DBName == "" {
+		return cons.MissingDBName
+	}
+
+	if in.Username == "" {
+		return cons.MissingUsername
+	}
+
+	if in.Password == "" {
+		return cons.MissingPassword
+	}
+
+	if in.DnBackupPath == "" {
+		return cons.MissingDnBackupPath
+	}
+
+	if in.BackupID == "" {
+		return cons.MissingBackupID
+	}
+
+	if in.Instance == "" {
+		return cons.MissingInstance
+	}
+	return nil
+}
diff --git a/pitr/agent/internal/pkg/mocks/opengauss.go b/pitr/agent/internal/pkg/mocks/opengauss.go
new file mode 100644
index 0000000..9b8f95f
--- /dev/null
+++ b/pitr/agent/internal/pkg/mocks/opengauss.go
@@ -0,0 +1,249 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: opengauss.go
+
+// Package mock_pkg is a generated GoMock package.
+package mock_pkg
+
+import (
+	reflect "reflect"
+
+	model "github.com/apache/shardingsphere-on-cloud/pitr/agent/internal/pkg/model"
+	gomock "github.com/golang/mock/gomock"
+)
+
+// MockIOpenGauss is a mock of IOpenGauss interface.
+type MockIOpenGauss struct {
+	ctrl     *gomock.Controller
+	recorder *MockIOpenGaussMockRecorder
+}
+
+// MockIOpenGaussMockRecorder is the mock recorder for MockIOpenGauss.
+type MockIOpenGaussMockRecorder struct {
+	mock *MockIOpenGauss
+}
+
+// NewMockIOpenGauss creates a new mock instance.
+func NewMockIOpenGauss(ctrl *gomock.Controller) *MockIOpenGauss {
+	mock := &MockIOpenGauss{ctrl: ctrl}
+	mock.recorder = &MockIOpenGaussMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockIOpenGauss) EXPECT() *MockIOpenGaussMockRecorder {
+	return m.recorder
+}
+
+// AddInstance mocks base method.
+func (m *MockIOpenGauss) AddInstance(backupPath, instance string) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "AddInstance", backupPath, instance)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// AddInstance indicates an expected call of AddInstance.
+func (mr *MockIOpenGaussMockRecorder) AddInstance(backupPath, instance interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddInstance", reflect.TypeOf((*MockIOpenGauss)(nil).AddInstance), backupPath, instance)
+}
+
+// AsyncBackup mocks base method.
+func (m *MockIOpenGauss) AsyncBackup(backupPath, instanceName, backupMode string, threadsNum uint8, dbPort uint16) (string, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "AsyncBackup", backupPath, instanceName, backupMode, threadsNum, dbPort)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// AsyncBackup indicates an expected call of AsyncBackup.
+func (mr *MockIOpenGaussMockRecorder) AsyncBackup(backupPath, instanceName, backupMode, threadsNum, dbPort interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AsyncBackup", reflect.TypeOf((*MockIOpenGauss)(nil).AsyncBackup), backupPath, instanceName, backupMode, threadsNum, dbPort)
+}
+
+// Auth mocks base method.
+func (m *MockIOpenGauss) Auth(user, password, dbName string, dbPort uint16) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Auth", user, password, dbName, dbPort)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Auth indicates an expected call of Auth.
+func (mr *MockIOpenGaussMockRecorder) Auth(user, password, dbName, dbPort interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Auth", reflect.TypeOf((*MockIOpenGauss)(nil).Auth), user, password, dbName, dbPort)
+}
+
+// CleanPgDataTemp mocks base method.
+func (m *MockIOpenGauss) CleanPgDataTemp() error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CleanPgDataTemp")
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// CleanPgDataTemp indicates an expected call of CleanPgDataTemp.
+func (mr *MockIOpenGaussMockRecorder) CleanPgDataTemp() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanPgDataTemp", reflect.TypeOf((*MockIOpenGauss)(nil).CleanPgDataTemp))
+}
+
+// DelBackup mocks base method.
+func (m *MockIOpenGauss) DelBackup(backupPath, instance, backupID string) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DelBackup", backupPath, instance, backupID)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// DelBackup indicates an expected call of DelBackup.
+func (mr *MockIOpenGaussMockRecorder) DelBackup(backupPath, instance, backupID interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelBackup", reflect.TypeOf((*MockIOpenGauss)(nil).DelBackup), backupPath, instance, backupID)
+}
+
+// DelInstance mocks base method.
+func (m *MockIOpenGauss) DelInstance(backupPath, instance string) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DelInstance", backupPath, instance)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// DelInstance indicates an expected call of DelInstance.
+func (mr *MockIOpenGaussMockRecorder) DelInstance(backupPath, instance interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelInstance", reflect.TypeOf((*MockIOpenGauss)(nil).DelInstance), backupPath, instance)
+}
+
+// Init mocks base method.
+func (m *MockIOpenGauss) Init(backupPath string) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Init", backupPath)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Init indicates an expected call of Init.
+func (mr *MockIOpenGaussMockRecorder) Init(backupPath interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockIOpenGauss)(nil).Init), backupPath)
+}
+
+// MvPgDataToTemp mocks base method.
+func (m *MockIOpenGauss) MvPgDataToTemp() error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "MvPgDataToTemp")
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// MvPgDataToTemp indicates an expected call of MvPgDataToTemp.
+func (mr *MockIOpenGaussMockRecorder) MvPgDataToTemp() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MvPgDataToTemp", reflect.TypeOf((*MockIOpenGauss)(nil).MvPgDataToTemp))
+}
+
+// MvTempToPgData mocks base method.
+func (m *MockIOpenGauss) MvTempToPgData() error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "MvTempToPgData")
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// MvTempToPgData indicates an expected call of MvTempToPgData.
+func (mr *MockIOpenGaussMockRecorder) MvTempToPgData() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MvTempToPgData", reflect.TypeOf((*MockIOpenGauss)(nil).MvTempToPgData))
+}
+
+// Restore mocks base method.
+func (m *MockIOpenGauss) Restore(backupPath, instance, backupID string) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Restore", backupPath, instance, backupID)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Restore indicates an expected call of Restore.
+func (mr *MockIOpenGaussMockRecorder) Restore(backupPath, instance, backupID interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Restore", reflect.TypeOf((*MockIOpenGauss)(nil).Restore), backupPath, instance, backupID)
+}
+
+// ShowBackup mocks base method.
+func (m *MockIOpenGauss) ShowBackup(backupPath, instanceName, backupID string) (*model.Backup, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ShowBackup", backupPath, instanceName, backupID)
+	ret0, _ := ret[0].(*model.Backup)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ShowBackup indicates an expected call of ShowBackup.
+func (mr *MockIOpenGaussMockRecorder) ShowBackup(backupPath, instanceName, backupID interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShowBackup", reflect.TypeOf((*MockIOpenGauss)(nil).ShowBackup), backupPath, instanceName, backupID)
+}
+
+// ShowBackupList mocks base method.
+func (m *MockIOpenGauss) ShowBackupList(backupPath, instanceName string) ([]*model.Backup, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ShowBackupList", backupPath, instanceName)
+	ret0, _ := ret[0].([]*model.Backup)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ShowBackupList indicates an expected call of ShowBackupList.
+func (mr *MockIOpenGaussMockRecorder) ShowBackupList(backupPath, instanceName interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShowBackupList", reflect.TypeOf((*MockIOpenGauss)(nil).ShowBackupList), backupPath, instanceName)
+}
+
+// Start mocks base method.
+func (m *MockIOpenGauss) Start() error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Start")
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Start indicates an expected call of Start.
+func (mr *MockIOpenGaussMockRecorder) Start() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockIOpenGauss)(nil).Start))
+}
+
+// Status mocks base method.
+func (m *MockIOpenGauss) Status() (string, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Status")
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Status indicates an expected call of Status.
+func (mr *MockIOpenGaussMockRecorder) Status() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockIOpenGauss)(nil).Status))
+}
+
+// Stop mocks base method.
+func (m *MockIOpenGauss) Stop() error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Stop")
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Stop indicates an expected call of Stop.
+func (mr *MockIOpenGaussMockRecorder) Stop() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockIOpenGauss)(nil).Stop))
+}
diff --git a/pitr/agent/internal/pkg/opengauss.go b/pitr/agent/internal/pkg/opengauss.go
index 0fd16b6..9d463e2 100644
--- a/pitr/agent/internal/pkg/opengauss.go
+++ b/pitr/agent/internal/pkg/opengauss.go
@@ -45,6 +45,7 @@ type (
 		Init(backupPath string) error
 		AddInstance(backupPath, instance string) error
 		DelInstance(backupPath, instance string) error
+		DelBackup(backupPath, instance, backupID string) error
 		Start() error
 		Stop() error
 		Status() (string, error)
@@ -57,6 +58,8 @@ type (
 	}
 )
 
+var _ IOpenGauss = (*openGauss)(nil)
+
 func NewOpenGauss(shell, pgData string, log logging.ILog) IOpenGauss {
 	dirs := strings.Split(pgData, "/")
 	dirs = append(dirs[0:len(dirs)-1], "temp")
@@ -146,8 +149,7 @@ func (og *openGauss) ShowBackup(backupPath, instanceName, backupID string) (*mod
 	return nil, fmt.Errorf("backupList[v=%+v],err=%w", list, cons.DataNotFound)
 }
 
-// nolint
-func (og *openGauss) delBackup(backupPath, instanceName, backupID string) error {
+func (og *openGauss) DelBackup(backupPath, instanceName, backupID string) error {
 	cmd := fmt.Sprintf(_delBackupFmt, backupPath, instanceName, backupID)
 	_, err := cmds.Exec(og.shell, cmd)
 	if err != nil {
diff --git a/pitr/agent/internal/pkg/opengauss_test.go b/pitr/agent/internal/pkg/opengauss_test.go
index ad3f7ab..9afa174 100644
--- a/pitr/agent/internal/pkg/opengauss_test.go
+++ b/pitr/agent/internal/pkg/opengauss_test.go
@@ -76,10 +76,10 @@ var _ = Describe("OpenGauss,requires opengauss environment", func() {
 			Fail("Timeout[60s]")
 			return
 		Del:
-			err = og.delBackup(data, instance, backupID)
+			err = og.DelBackup(data, instance, backupID)
 			Expect(err).To(BeNil())
 
-			err = og.delBackup(data, instance, backupID)
+			err = og.DelBackup(data, instance, backupID)
 			Expect(errors.Is(err, cons.CmdOperateFailed)).To(BeTrue())
 		})
 	})
diff --git a/pitr/agent/main.go b/pitr/agent/main.go
index 73bf84f..8c11df7 100644
--- a/pitr/agent/main.go
+++ b/pitr/agent/main.go
@@ -161,6 +161,7 @@ func SetupApp() {
 		r.Use(middleware.RequestIDChecker())
 
 		r.Post("/backup", handler.Backup)
+		r.Delete("/backup", handler.DeleteBackup)
 		r.Post("/restore", handler.Restore)
 		r.Post("/show", handler.Show)
 		r.Post("/show/list", handler.ShowList)
diff --git a/pitr/cli/internal/cmd/backup.go b/pitr/cli/internal/cmd/backup.go
index 1490c38..9ef1b63 100644
--- a/pitr/cli/internal/cmd/backup.go
+++ b/pitr/cli/internal/cmd/backup.go
@@ -116,10 +116,12 @@ func backup() error {
 
 	defer func() {
 		if err != nil {
-			logging.Info("Try to unlock cluster ...")
+			logging.Warn("Try to unlock cluster ...")
 			if err := proxy.Unlock(); err != nil {
 				logging.Error(fmt.Sprintf("Coz backup failed, try to unlock cluster, but still failed, err:%s", err.Error()))
 			}
+			logging.Warn("Try to delete backup data ...")
+			deleteBackupFiles(lsBackup)
 		}
 	}()
 
@@ -358,11 +360,11 @@ func checkBackupStatus(lsBackup *model.LsBackup) model.BackupStatus {
 func checkStatus(as pkg.IAgentServer, sn *model.StorageNode, dn *model.DataNode, dnCh chan *model.DataNode, pw progress.Writer) {
 	var (
 		// mark check status is done, time ticker should break.
-		done = make(chan bool)
+		done = make(chan struct{})
 		// time ticker, try to doCheck request every 2 seconds.
 		ticker = time.Tick(time.Second * 2)
 		// progress bar.
-		tracker = progress.Tracker{Message: fmt.Sprintf("Checking backup status  # %s:%d", sn.IP, AgentPort), Total: 0, Units: progress.UnitsDefault}
+		tracker = progress.Tracker{Message: fmt.Sprintf("Checking backup status  # %s:%d", sn.IP, sn.Port), Total: 0, Units: progress.UnitsDefault}
 	)
 
 	pw.AppendTracker(&tracker)
@@ -378,14 +380,14 @@ func checkStatus(as pkg.IAgentServer, sn *model.StorageNode, dn *model.DataNode,
 				dn.Status = status
 				dn.EndTime = time.Now().Unix()
 				dnCh <- dn
-				done <- true
+				done <- struct{}{}
 			}
 			if status == model.SsBackupStatusCompleted || status == model.SsBackupStatusFailed {
 				tracker.MarkAsDone()
 				dn.Status = status
 				dn.EndTime = time.Now().Unix()
 				dnCh <- dn
-				done <- true
+				done <- struct{}{}
 			}
 		}
 	}
@@ -412,3 +414,91 @@ func doCheck(as pkg.IAgentServer, sn *model.StorageNode, backupID string, retrie
 
 	return backupInfo.Status, nil
 }
+
+func deleteBackupFiles(lsBackup *model.LsBackup) {
+	var (
+		dataNodeMap = make(map[string]*model.DataNode)
+		totalNum    = len(lsBackup.SsBackup.StorageNodes)
+		resultCh    = make(chan *model.DeleteBackupResult, totalNum)
+	)
+	for _, dn := range lsBackup.DnList {
+		dataNodeMap[dn.IP] = dn
+	}
+
+	if totalNum == 0 {
+		logging.Info("No data node need to delete backup files")
+		return
+	}
+
+	pw := prettyoutput.NewPW(totalNum)
+	go pw.Render()
+
+	for _, sn := range lsBackup.SsBackup.StorageNodes {
+		sn := sn
+		dn, ok := dataNodeMap[sn.IP]
+		if !ok {
+			logging.Error(fmt.Sprintf("data node %s:%d not found, SKIPPED!", sn.IP, sn.Port))
+			continue
+		}
+		as := pkg.NewAgentServer(fmt.Sprintf("%s:%d", convertLocalhost(sn.IP), AgentPort))
+
+		go doDelete(as, sn, dn, resultCh, pw)
+	}
+
+	time.Sleep(time.Millisecond * 100)
+	for pw.IsRenderInProgress() {
+		time.Sleep(time.Millisecond * 100)
+	}
+	close(resultCh)
+
+	t := table.NewWriter()
+	t.SetOutputMirror(os.Stdout)
+	t.SetTitle("Delete Backup Files Result")
+	t.AppendHeader(table.Row{"#", "Node IP", "Node Port", "Result", "Message"})
+	t.SetColumnConfigs([]table.ColumnConfig{{Number: 5, WidthMax: 50}})
+
+	idx := 0
+	for result := range resultCh {
+		idx++
+		t.AppendRow([]interface{}{idx, result.IP, result.Port, result.Status, result.Msg})
+		t.AppendSeparator()
+	}
+
+	t.Render()
+
+	logging.Info("Delete backup files finished")
+}
+
+func doDelete(as pkg.IAgentServer, sn *model.StorageNode, dn *model.DataNode, resultCh chan *model.DeleteBackupResult, pw progress.Writer) {
+	var (
+		tracker = progress.Tracker{Message: fmt.Sprintf("Deleting backup files  # %s:%d", sn.IP, sn.Port), Total: 0, Units: progress.UnitsDefault}
+	)
+
+	pw.AppendTracker(&tracker)
+
+	in := &model.DeleteBackupIn{
+		DBPort:       sn.Port,
+		DBName:       sn.Database,
+		Username:     sn.Username,
+		Password:     sn.Password,
+		DnBackupPath: BackupPath,
+		BackupID:     dn.BackupID,
+		Instance:     defaultInstance,
+	}
+
+	r := &model.DeleteBackupResult{
+		IP:   sn.IP,
+		Port: sn.Port,
+	}
+
+	if err := as.DeleteBackup(in); err != nil {
+		r.Status = model.SsBackupStatusFailed
+		r.Msg = err.Error()
+		resultCh <- r
+		tracker.MarkAsErrored()
+	} else {
+		tracker.MarkAsDone()
+		r.Status = model.SsBackupStatusCompleted
+		resultCh <- r
+	}
+}
diff --git a/pitr/cli/internal/cmd/backup_test.go b/pitr/cli/internal/cmd/backup_test.go
index 61d05e4..6fbf352 100644
--- a/pitr/cli/internal/cmd/backup_test.go
+++ b/pitr/cli/internal/cmd/backup_test.go
@@ -153,9 +153,12 @@ var _ = Describe("Backup", func() {
 			}
 			as.EXPECT().Backup(gomock.Any()).Return("", nil)
 			dnCh := make(chan *model.DataNode, 10)
+
 			Expect(_execBackup(as, bak.SsBackup.StorageNodes[0], dnCh)).To(BeNil())
 			Expect(len(dnCh)).To(Equal(1))
+
 			as.EXPECT().Backup(gomock.Any()).Return("", xerr.NewCliErr("backup failed"))
+
 			Expect(_execBackup(as, bak.SsBackup.StorageNodes[0], dnCh)).ToNot(BeNil())
 			close(dnCh)
 			Expect(len(dnCh)).To(Equal(1))
@@ -208,7 +211,7 @@ var _ = Describe("Backup", func() {
 			monkey.UnpatchAll()
 		})
 
-		It("check error", func() {
+		It("check error 1", func() {
 			as.EXPECT().ShowDetail(gomock.Any()).Return(nil, errors.New("timeout")).AnyTimes()
 			Expect(checkBackupStatus(lsbackup)).To(Equal(model.SsBackupStatusFailed))
 		})
@@ -395,4 +398,40 @@ var _ = Describe("test backup mock", func() {
 			Expect(checkAgentServerStatus(ls)).To(BeFalse())
 		})
 	})
+
+	Context("test delete backup data", func() {
+		bak := &model.LsBackup{
+			Info: nil,
+			DnList: []*model.DataNode{
+				{
+					IP:   "test.delete.backup",
+					Port: 3306,
+				},
+			},
+			SsBackup: &model.SsBackup{
+				StorageNodes: []*model.StorageNode{
+					{
+						IP:   "test.delete.backup",
+						Port: 3306,
+					},
+				},
+			},
+		}
+		It("should delete failed", func() {
+			deleteBackupFiles(bak)
+		})
+
+		It("should delete success", func() {
+			ctrl := gomock.NewController(GinkgoT())
+			as := mock_pkg.NewMockIAgentServer(ctrl)
+			monkey.Patch(pkg.NewAgentServer, func(addr string) pkg.IAgentServer {
+				return as
+			})
+
+			defer monkey.UnpatchAll()
+			defer ctrl.Finish()
+			as.EXPECT().DeleteBackup(gomock.Any()).Return(nil)
+			deleteBackupFiles(bak)
+		})
+	})
 })
diff --git a/pitr/cli/internal/cmd/restore_test.go b/pitr/cli/internal/cmd/restore_test.go
index 44d4764..04a12e1 100644
--- a/pitr/cli/internal/cmd/restore_test.go
+++ b/pitr/cli/internal/cmd/restore_test.go
@@ -128,6 +128,7 @@ var _ = Describe("test restore", func() {
 		proxy.EXPECT().ImportMetaData(gomock.Any()).Return(nil)
 		as.EXPECT().CheckStatus().Return(nil)
 		as.EXPECT().Restore(gomock.Any()).Return(nil)
+
 		Expect(restore()).To(BeNil())
 	})
 
diff --git a/pitr/cli/internal/pkg/agent-server.go b/pitr/cli/internal/pkg/agent-server.go
index 0c1b744..9a35146 100644
--- a/pitr/cli/internal/pkg/agent-server.go
+++ b/pitr/cli/internal/pkg/agent-server.go
@@ -41,6 +41,7 @@ type agentServer struct {
 type IAgentServer interface {
 	CheckStatus() error
 	Backup(in *model.BackupIn) (string, error)
+	DeleteBackup(in *model.DeleteBackupIn) error
 	Restore(in *model.RestoreIn) error
 	ShowDetail(in *model.ShowDetailIn) (*model.BackupInfo, error)
 	ShowList(in *model.ShowListIn) ([]model.BackupInfo, error)
@@ -105,6 +106,7 @@ func (as *agentServer) Backup(in *model.BackupIn) (string, error) {
 	return out.Data.ID, nil
 }
 
+// nolint:dupl
 func (as *agentServer) Restore(in *model.RestoreIn) error {
 	url := fmt.Sprintf("%s%s", as.addr, as._apiRestore)
 
@@ -217,3 +219,33 @@ func (as *agentServer) ShowDiskSpace(in *model.DiskSpaceIn) (*model.DiskSpaceInf
 
 	return out, nil
 }
+
+// nolint:dupl
+func (as *agentServer) DeleteBackup(in *model.DeleteBackupIn) error {
+	url := fmt.Sprintf("%s%s", as.addr, as._apiBackup)
+
+	out := &model.DeleteBackupOut{}
+	r := httputils.NewRequest(context.Background(), http.MethodDelete, url)
+	r.Header(map[string]string{
+		"x-request-id": uuid.New().String(),
+		"content-type": "application/json",
+	})
+	r.Body(in)
+
+	httpCode, err := r.Send(out)
+	if err != nil {
+		efmt := "httputils.NewRequest[url=%s,body=%v,out=%v] return err=%s,wrap=%w"
+		return fmt.Errorf(efmt, url, in, out, err, xerr.NewCliErr(xerr.Unknown))
+	}
+
+	if httpCode != http.StatusOK {
+		return fmt.Errorf("unknown http status[code=%d],err=%w", httpCode, xerr.NewCliErr(xerr.InvalidHTTPStatus))
+	}
+
+	if out.Code != 0 {
+		asErr := xerr.NewAgentServerErr(out.Code, out.Msg)
+		return fmt.Errorf("agent server error[code=%d,msg=%s],err=%w", out.Code, out.Msg, asErr)
+	}
+
+	return nil
+}
diff --git a/pitr/cli/internal/pkg/mocks/agent-server.go b/pitr/cli/internal/pkg/mocks/agent-server.go
index 398aa18..e736033 100644
--- a/pitr/cli/internal/pkg/mocks/agent-server.go
+++ b/pitr/cli/internal/pkg/mocks/agent-server.go
@@ -1,20 +1,3 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
 // Code generated by MockGen. DO NOT EDIT.
 // Source: agent-server.go
 
@@ -80,6 +63,20 @@ func (mr *MockIAgentServerMockRecorder) CheckStatus() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckStatus", reflect.TypeOf((*MockIAgentServer)(nil).CheckStatus))
 }
 
+// DeleteBackup mocks base method.
+func (m *MockIAgentServer) DeleteBackup(in *model.DeleteBackupIn) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DeleteBackup", in)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// DeleteBackup indicates an expected call of DeleteBackup.
+func (mr *MockIAgentServerMockRecorder) DeleteBackup(in interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBackup", reflect.TypeOf((*MockIAgentServer)(nil).DeleteBackup), in)
+}
+
 // Restore mocks base method.
 func (m *MockIAgentServer) Restore(in *model.RestoreIn) error {
 	m.ctrl.T.Helper()
diff --git a/pitr/cli/internal/pkg/model/as_backup.go b/pitr/cli/internal/pkg/model/as_backup.go
index 3ba03aa..e4ffdae 100644
--- a/pitr/cli/internal/pkg/model/as_backup.go
+++ b/pitr/cli/internal/pkg/model/as_backup.go
@@ -39,6 +39,23 @@ type (
 		Msg  string    `json:"msg" validate:"required"`
 		Data BackupOut `json:"data"`
 	}
+
+	DeleteBackupIn struct {
+		DBPort   uint16 `json:"db_port"`
+		DBName   string `json:"db_name"`
+		Username string `json:"username"`
+		Password string `json:"password"`
+
+		DnBackupPath string `json:"dn_backup_path"`
+		BackupID     string `json:"backup_id"`
+		Instance     string `json:"instance"`
+	}
+
+	DeleteBackupOut struct {
+		Code int    `json:"code" validate:"required"`
+		Msg  string `json:"msg" validate:"required"`
+		Data string `json:"data"`
+	}
 )
 
 type AgentServerStatus struct {
@@ -47,8 +64,9 @@ type AgentServerStatus struct {
 	Status string `json:"status"`
 }
 
-type BackupResult struct {
+type DeleteBackupResult struct {
 	IP     string       `json:"ip"`
 	Port   uint16       `json:"port"`
 	Status BackupStatus `json:"status"`
+	Msg    string
 }