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
}