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/26 05:47:21 UTC
[shardingsphere-on-cloud] branch main updated: chore(pitr): restore support display progress
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 a413fed chore(pitr): restore support display progress
new c29abce Merge pull request #326 from Xu-Wentao/pitr
a413fed is described below
commit a413fed0b73e9c4fe51bb547e36fd9c9e27db1f1
Author: xuwentao <cu...@yahoo.com>
AuthorDate: Tue Apr 25 18:46:40 2023 +0800
chore(pitr): restore support display progress
---
pitr/agent/internal/handler/restore.go | 9 +-
pitr/agent/main.go | 2 +-
pitr/cli/internal/cmd/backup.go | 23 ++--
pitr/cli/internal/cmd/restore.go | 146 +++++++++++++--------
pitr/cli/internal/cmd/restore_test.go | 66 ++++++----
pitr/cli/internal/cmd/root.go | 8 +-
pitr/cli/internal/pkg/model/as_backup.go | 1 +
pitr/cli/internal/pkg/model/as_restore.go | 6 +
pitr/cli/internal/pkg/model/const.go | 1 +
.../pkg/prettyoutput/prettyoutput_suite_test.go | 30 +++++
pitr/cli/pkg/prettyoutput/progress_test.go | 56 ++++++++
11 files changed, 244 insertions(+), 104 deletions(-)
diff --git a/pitr/agent/internal/handler/restore.go b/pitr/agent/internal/handler/restore.go
index 32d2045..ff1bb33 100644
--- a/pitr/agent/internal/handler/restore.go
+++ b/pitr/agent/internal/handler/restore.go
@@ -63,11 +63,13 @@ func Restore(ctx *fiber.Ctx) (err error) {
}
}()
+ // move pgdata to temp
if err = pkg.OG.MvPgDataToTemp(); err != nil {
err = fmt.Errorf("pkg.OG.MvPgDataToTemp return err=%w", err)
return
}
+ // restore data from backup
if err = pkg.OG.Restore(in.DnBackupPath, in.Instance, in.DnBackupID); err != nil {
efmt := "pkg.OG.Restore failure[path=%s,instance=%s,backupID=%s],err=%w"
err = fmt.Errorf(efmt, in.DnBackupPath, in.Instance, in.DnBackupID, err)
@@ -78,6 +80,7 @@ func Restore(ctx *fiber.Ctx) (err error) {
return
}
+ // clean temp
if err = pkg.OG.CleanPgDataTemp(); err != nil {
err = fmt.Errorf("pkg.OG.CleanPgDataTemp return err=%w", err)
return
@@ -88,9 +91,5 @@ func Restore(ctx *fiber.Ctx) (err error) {
return
}
- if err = responder.Success(ctx, nil); err != nil {
- err = fmt.Errorf("responder failure,err=%s,wrap=%w", err, cons.Internal)
- return nil
- }
- return
+ return responder.Success(ctx, nil)
}
diff --git a/pitr/agent/main.go b/pitr/agent/main.go
index 805874b..73bf84f 100644
--- a/pitr/agent/main.go
+++ b/pitr/agent/main.go
@@ -92,7 +92,7 @@ func main() {
panic(fmt.Errorf("PGDATA:%s the database directory does not exist", pgData))
}
- pgData := strings.Trim(pgData, " ")
+ pgData = strings.Trim(pgData, " ")
if strings.HasSuffix(pgData, "/") {
dirs := strings.Split(pgData, "/")
dirs = dirs[0 : len(dirs)-1]
diff --git a/pitr/cli/internal/cmd/backup.go b/pitr/cli/internal/cmd/backup.go
index 9d57763..1490c38 100644
--- a/pitr/cli/internal/cmd/backup.go
+++ b/pitr/cli/internal/cmd/backup.go
@@ -103,21 +103,22 @@ func init() {
// 7. Double check backups all finished
func backup() error {
var err error
+ var lsBackup *model.LsBackup
proxy, err := pkg.NewShardingSphereProxy(Username, Password, pkg.DefaultDBName, Host, Port)
if err != nil {
- return xerr.NewCliErr("create ss-proxy connect failed")
+ return xerr.NewCliErr("Create ss-proxy connect failed")
}
ls, err := pkg.NewLocalStorage(pkg.DefaultRootDir())
if err != nil {
- return xerr.NewCliErr("create local storage failed")
+ return xerr.NewCliErr("Create local storage failed")
}
defer func() {
if err != nil {
- logging.Info("try to unlock cluster ...")
+ logging.Info("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.Error(fmt.Sprintf("Coz backup failed, try to unlock cluster, but still failed, err:%s", err.Error()))
}
}
}()
@@ -126,12 +127,12 @@ func backup() error {
logging.Info("Starting lock cluster ...")
err = proxy.LockForBackup()
if err != nil {
- return xerr.NewCliErr("lock for backup failed")
+ return xerr.NewCliErr("Lock for backup failed")
}
// Step2. Get cluster info and save local backup info
logging.Info("Starting export metadata ...")
- lsBackup, err := exportData(proxy, ls)
+ lsBackup, err = exportData(proxy, ls)
if err != nil {
return xerr.NewCliErr(fmt.Sprintf("export backup data failed, err:%s", err.Error()))
}
@@ -140,7 +141,7 @@ func backup() error {
// Step3. Check agent server status
logging.Info("Checking agent server status...")
if available := checkAgentServerStatus(lsBackup); !available {
- err = xerr.NewCliErr("one or more agent server are not available.")
+ err = xerr.NewCliErr("One or more agent server are not available.")
return err
}
@@ -302,6 +303,11 @@ func checkBackupStatus(lsBackup *model.LsBackup) model.BackupStatus {
dnResult = make([]*model.DataNode, 0)
)
+ if totalNum == 0 {
+ logging.Info("No data node need to backup")
+ return model.SsBackupStatusCanceled
+ }
+
for _, dn := range lsBackup.DnList {
dataNodeMap[dn.IP] = dn
}
@@ -318,9 +324,6 @@ func checkBackupStatus(lsBackup *model.LsBackup) model.BackupStatus {
// wait for all data node backup finished
time.Sleep(time.Millisecond * 100)
for pw.IsRenderInProgress() {
- if pw.LengthActive() == 0 {
- pw.Stop()
- }
time.Sleep(time.Millisecond * 100)
}
diff --git a/pitr/cli/internal/cmd/restore.go b/pitr/cli/internal/cmd/restore.go
index 88d1623..6c7a6a6 100644
--- a/pitr/cli/internal/cmd/restore.go
+++ b/pitr/cli/internal/cmd/restore.go
@@ -19,17 +19,19 @@ package cmd
import (
"fmt"
+ "os"
"strings"
- "sync"
+ "time"
"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg"
"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/model"
"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/xerr"
- "github.com/spf13/pflag"
-
- "github.com/spf13/cobra"
-
"github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/logging"
+ "github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/prettyoutput"
+ "github.com/jedib0t/go-pretty/v6/progress"
+ "github.com/jedib0t/go-pretty/v6/table"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -156,82 +158,112 @@ func checkDatabaseExist(proxy pkg.IShardingSphereProxy, bak *model.LsBackup) err
// get user input to confirm
prompt := fmt.Sprintf(
- "Detected that the database [%s] already exists in shardingsphere-proxy metadata.\n"+
- "The logic database will be DROPPED and then insert backup's metadata into shardingsphere-proxy after restoring the backup data.\n"+
- "PLEASE MAKE SURE OF THIS ACTION, CONTINUE? (Y|N)\n", strings.Join(databaseNamesExist, ","))
+ "Detected That The Database [%s] Already Exists In ShardingSphere-Proxy Metadata.\n"+
+ "The Logic Database Will Be DROPPED And Then Insert Backup's Metadata Into ShardingSphere-Proxy After Restoring The Backup Data.\n"+
+ "Are you sure to continue? (Y|N)", strings.Join(databaseNamesExist, ","))
return getUserApproveInTerminal(prompt)
}
+func restoreDataToSSProxy(proxy pkg.IShardingSphereProxy, lsBackup *model.LsBackup) error {
+ // drop database if exists
+ for _, shardingDBName := range databaseNamesExist {
+ logging.Info(fmt.Sprintf("Dropping database: [%s] ...", shardingDBName))
+ if err := proxy.DropDatabase(shardingDBName); err != nil {
+ return xerr.NewCliErr(fmt.Sprintf("drop database failed:%s", err.Error()))
+ }
+ }
+
+ // import metadata
+ if err := proxy.ImportMetaData(lsBackup.SsBackup.ClusterInfo); err != nil {
+ return xerr.NewCliErr(fmt.Sprintf("Import metadata to ss-proxy failed:%s", err.Error()))
+ }
+
+ return nil
+}
+
func execRestore(lsBackup *model.LsBackup) error {
var (
- wg sync.WaitGroup
- storageNodes = lsBackup.SsBackup.StorageNodes
- dataNodeMap = make(map[string]*model.DataNode)
- failedCh = make(chan error, len(storageNodes))
+ totalNum = len(lsBackup.SsBackup.StorageNodes)
+ dataNodeMap = make(map[string]*model.DataNode)
+ resultCh = make(chan *model.RestoreResult, totalNum)
+ dnResult = make([]*model.RestoreResult, 0)
+ restoreFinalStatus = "Completed"
)
for _, dataNode := range lsBackup.DnList {
dataNodeMap[dataNode.IP] = dataNode
}
- for _, storageNode := range storageNodes {
- wg.Add(1)
- storageNode := storageNode
- agentHost := storageNode.IP
- if agentHost == "127.0.0.1" {
- agentHost = Host
- }
- as := pkg.NewAgentServer(fmt.Sprintf("%s:%d", agentHost, AgentPort))
- dataNode, ok := dataNodeMap[storageNode.IP]
- if !ok {
- return xerr.NewCliErr(fmt.Sprintf("data node not found:%s", storageNode.IP))
- }
- go func() {
- defer wg.Done()
- _execRestore(as, storageNode, dataNode.BackupID, failedCh)
- }()
- }
- wg.Wait()
- close(failedCh)
- if len(failedCh) > 0 {
- var errMsg string
- for err := range failedCh {
- errMsg += err.Error() + "\n"
+ if totalNum == 0 {
+ return xerr.NewCliErr(fmt.Sprintf("no storage node found, please check backup record [%s].", lsBackup.Info.ID))
+ }
+
+ pw := prettyoutput.NewPW(totalNum)
+ go pw.Render()
+ for i := 0; i < totalNum; i++ {
+ sn := lsBackup.SsBackup.StorageNodes[i]
+ dn := dataNodeMap[sn.IP]
+ as := pkg.NewAgentServer(fmt.Sprintf("%s:%d", convertLocalhost(sn.IP), AgentPort))
+ go doRestore(as, sn, dn.BackupID, resultCh, pw)
+ }
+
+ time.Sleep(time.Millisecond * 100)
+ for pw.IsRenderInProgress() {
+ time.Sleep(time.Millisecond * 100)
+ }
+
+ close(resultCh)
+
+ for result := range resultCh {
+ dnResult = append(dnResult, result)
+ if result.Status != "Completed" {
+ restoreFinalStatus = "Failed"
}
- return xerr.NewCliErr(errMsg)
}
+
+ // print result formatted
+ t := table.NewWriter()
+ t.SetOutputMirror(os.Stdout)
+ t.SetTitle("Restore Task Result: %s", restoreFinalStatus)
+ t.AppendHeader(table.Row{"#", "Data Node IP", "Data Node Port", "Result"})
+
+ for i, dn := range dnResult {
+ t.AppendRow([]interface{}{i + 1, dn.IP, dn.Port, dn.Status})
+ t.AppendSeparator()
+ }
+
+ t.Render()
+
return nil
}
-func _execRestore(as pkg.IAgentServer, node *model.StorageNode, backupID string, failedCh chan error) {
+func doRestore(as pkg.IAgentServer, sn *model.StorageNode, backupID string, resultCh chan *model.RestoreResult, pw progress.Writer) {
+ tracker := &progress.Tracker{Message: fmt.Sprintf("Restore data to openGauss: %s", sn.IP)}
+ result := ""
+
in := &model.RestoreIn{
- DBPort: node.Port,
- DBName: node.Database,
- Username: node.Username,
- Password: node.Password,
+ DBPort: sn.Port,
+ DBName: sn.Database,
+ Username: sn.Username,
+ Password: sn.Password,
Instance: defaultInstance,
DnBackupPath: BackupPath,
DnBackupID: backupID,
}
- if err := as.Restore(in); err != nil {
- failedCh <- xerr.NewCliErr(fmt.Sprintf("restore node:[IP:%s] failed:%s", node.IP, err.Error()))
- }
-}
+ pw.AppendTracker(tracker)
-func restoreDataToSSProxy(proxy pkg.IShardingSphereProxy, lsBackup *model.LsBackup) error {
- // drop database if exists
- for _, shardingDBName := range databaseNamesExist {
- logging.Info(fmt.Sprintf("Dropping database: [%s] ...", shardingDBName))
- if err := proxy.DropDatabase(shardingDBName); err != nil {
- return xerr.NewCliErr(fmt.Sprintf("drop database failed:%s", err.Error()))
- }
+ if err := as.Restore(in); err != nil {
+ tracker.MarkAsErrored()
+ result = "Failed"
+ } else {
+ tracker.MarkAsDone()
+ result = "Completed"
}
- // import metadata
- if err := proxy.ImportMetaData(lsBackup.SsBackup.ClusterInfo); err != nil {
- return xerr.NewCliErr(fmt.Sprintf("Import metadata to ss-proxy failed:%s", err.Error()))
+ resultCh <- &model.RestoreResult{
+ IP: sn.IP,
+ Port: sn.Port,
+ Status: result,
}
-
- return nil
}
diff --git a/pitr/cli/internal/cmd/restore_test.go b/pitr/cli/internal/cmd/restore_test.go
index fd1898f..44d4764 100644
--- a/pitr/cli/internal/cmd/restore_test.go
+++ b/pitr/cli/internal/cmd/restore_test.go
@@ -20,6 +20,7 @@ package cmd
import (
"reflect"
+ "time"
"bou.ke/monkey"
"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg"
@@ -59,14 +60,21 @@ var _ = Describe("Restore", func() {
})
})
})
+
var _ = Describe("test restore", func() {
var (
proxy *mock_pkg.MockIShardingSphereProxy
ls *mock_pkg.MockILocalStorage
as *mock_pkg.MockIAgentServer
bak = &model.LsBackup{
- Info: nil,
- DnList: nil,
+ Info: &model.BackupMetaInfo{
+ ID: "backup-id-1",
+ },
+ DnList: []*model.DataNode{
+ {
+ IP: "127.0.0.1",
+ },
+ },
SsBackup: &model.SsBackup{
Status: "",
ClusterInfo: &model.ClusterInfo{
@@ -77,17 +85,13 @@ var _ = Describe("test restore", func() {
},
SnapshotInfo: nil,
},
- StorageNodes: nil,
+ StorageNodes: []*model.StorageNode{
+ {
+ IP: "127.0.0.1",
+ },
+ },
},
}
- sn = &model.StorageNode{
- IP: "127.0.0.1",
- Port: 3306,
- Username: "",
- Password: "",
- Database: "",
- Remark: "",
- }
)
BeforeEach(func() {
@@ -114,24 +118,16 @@ var _ = Describe("test restore", func() {
Expect(checkDatabaseExist(proxy, bak)).To(BeNil())
})
- It("test exec restore", func() {
- failedCh := make(chan error, 1)
- as.EXPECT().Restore(gomock.Any()).Return(nil)
- _execRestore(as, sn, "backup-id", failedCh)
- close(failedCh)
- Expect(<-failedCh).To(BeNil())
- })
-
It("test exec restore main func", func() {
// patch ReadByID of mock ls
- monkey.PatchInstanceMethod(reflect.TypeOf(ls), "ReadByID", func(_ *mock_pkg.MockILocalStorage, _ string) (*model.LsBackup, error) {
- return bak, nil
- })
- // mock ExportMetaData and return a *ClusterInfo with bak in it
- proxy.EXPECT().ExportMetaData().Return(bak.SsBackup.ClusterInfo, nil)
- // mock ImportMetaData and return nil
- proxy.EXPECT().ImportMetaData(gomock.Any()).Return(nil)
+ monkey.PatchInstanceMethod(reflect.TypeOf(ls), "ReadByID", func(_ *mock_pkg.MockILocalStorage, _ string) (*model.LsBackup, error) { return bak, nil })
+ monkey.Patch(pkg.NewAgentServer, func(_ string) pkg.IAgentServer { return as })
+
RecordID = "backup-id"
+ proxy.EXPECT().ExportMetaData().Return(&model.ClusterInfo{}, nil)
+ proxy.EXPECT().ImportMetaData(gomock.Any()).Return(nil)
+ as.EXPECT().CheckStatus().Return(nil)
+ as.EXPECT().Restore(gomock.Any()).Return(nil)
Expect(restore()).To(BeNil())
})
@@ -142,7 +138,6 @@ var _ = Describe("test restore", func() {
// exec getUserApproveInTerminal
Expect(getUserApproveInTerminal("")).To(Equal(xerr.NewCliErr("User abort")))
})
- // TODO test user approve, how to patch os.Stdin?
})
Context("restore data to ss proxy", func() {
@@ -160,4 +155,21 @@ var _ = Describe("test restore", func() {
})
})
+ Context("test exec restore", func() {
+ It("should be success", func() {
+ ctrl := gomock.NewController(GinkgoT())
+ as := mock_pkg.NewMockIAgentServer(ctrl)
+ monkey.Patch(pkg.NewAgentServer, func(_ string) pkg.IAgentServer {
+ return as
+ })
+ defer func() {
+ ctrl.Finish()
+ monkey.UnpatchAll()
+ }()
+ as.EXPECT().Restore(gomock.Any()).Do(func(_ *model.RestoreIn) {
+ time.Sleep(3 * time.Second)
+ }).Return(nil)
+ Expect(execRestore(bak)).To(BeNil())
+ })
+ })
})
diff --git a/pitr/cli/internal/cmd/root.go b/pitr/cli/internal/cmd/root.go
index 3d763d6..6df5fc7 100644
--- a/pitr/cli/internal/cmd/root.go
+++ b/pitr/cli/internal/cmd/root.go
@@ -97,20 +97,20 @@ func checkAgentServerStatus(lsBackup *model.LsBackup) bool {
sn := node
as := pkg.NewAgentServer(fmt.Sprintf("%s:%d", convertLocalhost(sn.IP), AgentPort))
if err := as.CheckStatus(); err != nil {
- statusList = append(statusList, &model.AgentServerStatus{IP: sn.IP, Status: "Unavailable"})
+ statusList = append(statusList, &model.AgentServerStatus{IP: sn.IP, Port: sn.Port, Status: "Unavailable"})
available = false
} else {
- statusList = append(statusList, &model.AgentServerStatus{IP: sn.IP, Status: "Available"})
+ statusList = append(statusList, &model.AgentServerStatus{IP: sn.IP, Port: sn.Port, Status: "Available"})
}
}
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.SetTitle("Agent Server Status")
- t.AppendHeader(table.Row{"#", "Agent Server IP", "Status"})
+ t.AppendHeader(table.Row{"#", "Agent Server IP", "Agent Server Port", "Status"})
for i, s := range statusList {
- t.AppendRow([]interface{}{i + 1, s.IP, s.Status})
+ t.AppendRow([]interface{}{i + 1, s.IP, s.Port, s.Status})
t.AppendSeparator()
}
diff --git a/pitr/cli/internal/pkg/model/as_backup.go b/pitr/cli/internal/pkg/model/as_backup.go
index ee3078f..3ba03aa 100644
--- a/pitr/cli/internal/pkg/model/as_backup.go
+++ b/pitr/cli/internal/pkg/model/as_backup.go
@@ -43,6 +43,7 @@ type (
type AgentServerStatus struct {
IP string `json:"ip"`
+ Port uint16 `json:"port"`
Status string `json:"status"`
}
diff --git a/pitr/cli/internal/pkg/model/as_restore.go b/pitr/cli/internal/pkg/model/as_restore.go
index 2381de7..3033559 100644
--- a/pitr/cli/internal/pkg/model/as_restore.go
+++ b/pitr/cli/internal/pkg/model/as_restore.go
@@ -32,4 +32,10 @@ type (
Code int `json:"code" validate:"required"`
Msg string `json:"msg" validate:"required"`
}
+
+ RestoreResult struct {
+ IP string `json:"ip"`
+ Port uint16 `json:"port"`
+ Status string `json:"status"`
+ }
)
diff --git a/pitr/cli/internal/pkg/model/const.go b/pitr/cli/internal/pkg/model/const.go
index d745929..58452f8 100644
--- a/pitr/cli/internal/pkg/model/const.go
+++ b/pitr/cli/internal/pkg/model/const.go
@@ -26,6 +26,7 @@ const (
SsBackupStatusCompleted BackupStatus = "Completed"
SsBackupStatusFailed BackupStatus = "Failed"
SsBackupStatusCheckError BackupStatus = "CheckError"
+ SsBackupStatusCanceled BackupStatus = "Canceled"
BDBackModeFull DBBackupMode = "FULL"
DBBackModePTrack DBBackupMode = "PTRACK"
diff --git a/pitr/cli/pkg/prettyoutput/prettyoutput_suite_test.go b/pitr/cli/pkg/prettyoutput/prettyoutput_suite_test.go
new file mode 100644
index 0000000..d18bc0f
--- /dev/null
+++ b/pitr/cli/pkg/prettyoutput/prettyoutput_suite_test.go
@@ -0,0 +1,30 @@
+/*
+ * 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 prettyoutput_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestPrettyOutput(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "PrettyOutput Suite")
+}
diff --git a/pitr/cli/pkg/prettyoutput/progress_test.go b/pitr/cli/pkg/prettyoutput/progress_test.go
new file mode 100644
index 0000000..b89c21a
--- /dev/null
+++ b/pitr/cli/pkg/prettyoutput/progress_test.go
@@ -0,0 +1,56 @@
+/*
+ * 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 prettyoutput_test
+
+import (
+ "time"
+
+ "github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/prettyoutput"
+ "github.com/jedib0t/go-pretty/v6/progress"
+ . "github.com/onsi/ginkgo/v2"
+)
+
+var _ = Describe("Progress", func() {
+ It("test style", func() {
+ pw := prettyoutput.NewPW(5)
+ go pw.Render()
+
+ for i := 0; i < 5; i++ {
+ go func() {
+ tracker := &progress.Tracker{
+ Message: "test",
+ Total: 10,
+ Units: progress.UnitsDefault,
+ }
+
+ pw.AppendTracker(tracker)
+ ticker := time.Tick(time.Millisecond * 100)
+ for !tracker.IsDone() {
+ for range ticker {
+ tracker.Increment(1)
+ }
+ }
+ }()
+ }
+
+ time.Sleep(time.Millisecond * 100)
+ for pw.IsRenderInProgress() {
+ time.Sleep(time.Millisecond * 100)
+ }
+ })
+})