From 518248f66db8114b2e0ab37948765e7b59543e97 Mon Sep 17 00:00:00 2001
From: Payam Qorbanpour <payamqorbanpour@gmail.com>
Date: Sat, 29 Oct 2022 17:48:33 +0330
Subject: [PATCH 1/6] Add periodic get migration progress

---
 go.mod                |   1 +
 go.sum                |   3 +
 pkg/paas/migration.go | 136 ++++++++++++++++++++++++++++++++++++++----
 3 files changed, 129 insertions(+), 11 deletions(-)

diff --git a/go.mod b/go.mod
index 62a042a..5ab9b98 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@ require (
 	github.com/docker/docker v20.10.0+incompatible // indirect
 	github.com/fsouza/go-dockerclient v1.6.6 // indirect
 	github.com/gonum/graph v0.0.0-20190426092945-678096d81a4b // indirect
+	github.com/gosuri/uilive v0.0.4
 	github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf
 	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/moby/buildkit v0.8.0 // indirect
diff --git a/go.sum b/go.sum
index 95e48c3..40b8032 100644
--- a/go.sum
+++ b/go.sum
@@ -665,6 +665,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
 github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
+github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY=
+github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI=
 github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
@@ -834,6 +836,7 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
diff --git a/pkg/paas/migration.go b/pkg/paas/migration.go
index d8114c0..4b367df 100644
--- a/pkg/paas/migration.go
+++ b/pkg/paas/migration.go
@@ -8,14 +8,16 @@ import (
 	"io"
 	"log"
 	"net/http"
+	"net/url"
 	"os"
 	"strconv"
 	"strings"
-	"net/url"
+	"time"
 
 	"github.com/arvancloud/cli/pkg/api"
 	"github.com/arvancloud/cli/pkg/config"
 	"github.com/arvancloud/cli/pkg/utl"
+	"github.com/gosuri/uilive"
 	"github.com/olekukonko/tablewriter"
 	"k8s.io/client-go/rest"
 
@@ -30,6 +32,16 @@ const (
 	yellowColor       = "\033[33m"
 	resetColor        = "\033[0m"
 	bamdad            = "ba1"
+	interval          = 2
+)
+
+type State string
+
+const (
+	Queued    State = "Queued"
+	Doing           = "Doing"
+	Completed       = "Completed"
+	Failed          = "Failed"
 )
 
 type Request struct {
@@ -55,6 +67,29 @@ type ZoneInfo struct {
 	Gateway  string    `json:"gateway"`
 }
 
+type StepData struct {
+	Time     time.Time
+	Message  string
+	Percent  int
+	Response Response
+}
+
+type Step struct {
+	Order int
+	Step  string
+	Title string
+	State string
+	Data  StepData
+}
+
+type ProgressResponse struct {
+	State       State
+	Source      string
+	Destination string
+	Namespace   string
+	Steps       []Step
+}
+
 type Response struct {
 	Source      ZoneInfo `json:"source"`
 	Destination ZoneInfo `json:"destination"`
@@ -216,18 +251,66 @@ func (v confirmationValidator) confirmationValidate(input string) (bool, error)
 
 // migrate sends migration request and displays response.
 func migrate(request Request) error {
-	response, err := httpPost(migrationEndpoint, request)
-	if err != nil {
-		failureOutput()
+	postResponse, err := httpPost(migrationEndpoint, request)
+	if err != nil || postResponse.StatusCode != http.StatusCreated {
+		failureOutput(fmt.Sprint(postResponse.StatusCode))
 		return err
 	}
-	successOutput(response)
-	
+
+	writer := uilive.New()
+	writer.Start()
+	defer writer.Stop()
+
+	stopChannel := make(chan bool, 1)
+	doEvery(interval*time.Second, stopChannel, func() {
+		response, _ := httpGet(migrationEndpoint)
+
+		sprintResponse(*response, writer)
+
+		if response.State == Completed {
+			close(stopChannel)
+
+			successOutput(&response.Steps[len(response.Steps)-1].Data.Response)
+		}
+
+		if response.State == Failed {
+			failureOutput(response.Steps[len(response.Steps)-1].Data.Message)
+		}
+	})
+
+	return nil
+}
+
+// doEvery runs given function in periods of 'd' and stops using stopChannel.
+func doEvery(d time.Duration, stopChannel chan bool, f func()) {
+	ticker := time.NewTicker(d)
+
+	for {
+		select {
+		case <-ticker.C:
+			f()
+		case <-stopChannel:
+			ticker.Stop()
+			return
+		}
+	}
+}
+
+// sprintResponse displays steps of migration.
+func sprintResponse(response ProgressResponse, w *uilive.Writer) error {
+	responseStr := fmt.Sprintf("Migrating namespace \"%s\" from \"%s\" to \"%s\" started\n", response.Namespace, response.Source, response.Destination)
+	for _, s := range response.Steps {
+		responseStr += fmt.Sprintf("\t%s... %s %s\n", s.Title, s.State, s.Data.Message)
+	}
+
+	fmt.Fprintf(w, "%s", responseStr)
+	time.Sleep(time.Second * 1)
+
 	return nil
 }
 
 // httpPost sends POST request to inserted url.
-func httpPost(endpoint string, payload interface{}) (*Response, error) {
+func httpPost(endpoint string, payload interface{}) (*http.Response, error) {
 	requestBody, err := json.Marshal(payload)
 	if err != nil {
 		return nil, err
@@ -238,7 +321,38 @@ func httpPost(endpoint string, payload interface{}) (*Response, error) {
 		return nil, fmt.Errorf("invalid config")
 	}
 
-	httpReq, err := http.NewRequest(http.MethodPost, arvanURL.Scheme + "://" + arvanURL.Host+endpoint, bytes.NewBuffer(requestBody))
+	httpReq, err := http.NewRequest(http.MethodPost, arvanURL.Scheme+"://"+arvanURL.Host+endpoint, bytes.NewBuffer(requestBody))
+	if err != nil {
+		return nil, err
+	}
+	apikey := arvanConfig.GetApiKey()
+	if apikey != "" {
+		httpReq.Header.Add("Authorization", apikey)
+	}
+
+	httpReq.Header.Add("accept", "application/json")
+	httpReq.Header.Add("User-Agent", rest.DefaultKubernetesUserAgent())
+	httpResp, err := http.DefaultClient.Do(httpReq)
+	if err != nil {
+		return nil, err
+	}
+
+	if httpResp.StatusCode != http.StatusOK {
+		return nil, errors.New("server error. try again later")
+	}
+
+	return httpResp, nil
+}
+
+// httpGet sends GET request to inserted url.
+func httpGet(endpoint string) (*ProgressResponse, error) {
+	arvanConfig := config.GetConfigInfo()
+	arvanURL, err := url.Parse(arvanConfig.GetServer())
+	if err != nil {
+		return nil, fmt.Errorf("invalid config")
+	}
+
+	httpReq, err := http.NewRequest(http.MethodGet, arvanURL.Scheme+"://"+arvanURL.Host+endpoint, bytes.NewBuffer([]byte{}))
 	if err != nil {
 		return nil, err
 	}
@@ -266,7 +380,7 @@ func httpPost(endpoint string, payload interface{}) (*Response, error) {
 	}
 
 	// parse response
-	var response Response
+	var response ProgressResponse
 	err = json.Unmarshal(responseBody, &response)
 	if err != nil {
 		return nil, err
@@ -275,8 +389,8 @@ func httpPost(endpoint string, payload interface{}) (*Response, error) {
 }
 
 // failureOutput displays failure output.
-func failureOutput() {
-	fmt.Println("failed to migrate")
+func failureOutput(message string) {
+	fmt.Println("failed to migrate", message)
 }
 
 // successOutput displays success output.
-- 
GitLab


From a10aefcc38c50b549f31b183b2b46791c5783568 Mon Sep 17 00:00:00 2001
From: Payam Qorbanpour <payamqorbanpour@gmail.com>
Date: Sun, 30 Oct 2022 15:13:43 +0330
Subject: [PATCH 2/6] Add tabwriter to display lines in column

---
 pkg/paas/migration.go | 38 +++++++++++++++++++++++++-------------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/pkg/paas/migration.go b/pkg/paas/migration.go
index 4b367df..f449c88 100644
--- a/pkg/paas/migration.go
+++ b/pkg/paas/migration.go
@@ -12,17 +12,18 @@ import (
 	"os"
 	"strconv"
 	"strings"
+	"text/tabwriter"
 	"time"
 
 	"github.com/arvancloud/cli/pkg/api"
 	"github.com/arvancloud/cli/pkg/config"
 	"github.com/arvancloud/cli/pkg/utl"
+
 	"github.com/gosuri/uilive"
 	"github.com/olekukonko/tablewriter"
-	"k8s.io/client-go/rest"
-
 	"github.com/openshift/oc/pkg/helpers/term"
 	"github.com/spf13/cobra"
+	"k8s.io/client-go/rest"
 )
 
 const (
@@ -38,7 +39,7 @@ const (
 type State string
 
 const (
-	Queued    State = "Queued"
+	Pending   State = "Pending"
 	Doing           = "Doing"
 	Completed       = "Completed"
 	Failed          = "Failed"
@@ -252,23 +253,34 @@ func (v confirmationValidator) confirmationValidate(input string) (bool, error)
 // migrate sends migration request and displays response.
 func migrate(request Request) error {
 	postResponse, err := httpPost(migrationEndpoint, request)
-	if err != nil || postResponse.StatusCode != http.StatusCreated {
-		failureOutput(fmt.Sprint(postResponse.StatusCode))
+	if err != nil {
+		failureOutput(err.Error())
 		return err
 	}
 
-	writer := uilive.New()
-	writer.Start()
-	defer writer.Stop()
+	if postResponse.StatusCode != http.StatusCreated {
+		failureOutput(fmt.Sprint(postResponse.StatusCode))
+	}
+
+	// init writer to update lines
+	uiliveWriter := uilive.New()
+	uiliveWriter.Start()
+
+	// init writer to display lines in column
+	tabWriter := new(tabwriter.Writer)
+	tabWriter.Init(uiliveWriter, 0, 8, 0, '\t', 0)
 
 	stopChannel := make(chan bool, 1)
+
 	doEvery(interval*time.Second, stopChannel, func() {
 		response, _ := httpGet(migrationEndpoint)
 
-		sprintResponse(*response, writer)
+		sprintResponse(*response, tabWriter)
 
 		if response.State == Completed {
 			close(stopChannel)
+			tabWriter.Flush()
+			uiliveWriter.Stop()
 
 			successOutput(&response.Steps[len(response.Steps)-1].Data.Response)
 		}
@@ -297,14 +309,14 @@ func doEvery(d time.Duration, stopChannel chan bool, f func()) {
 }
 
 // sprintResponse displays steps of migration.
-func sprintResponse(response ProgressResponse, w *uilive.Writer) error {
-	responseStr := fmt.Sprintf("Migrating namespace \"%s\" from \"%s\" to \"%s\" started\n", response.Namespace, response.Source, response.Destination)
+func sprintResponse(response ProgressResponse, w io.Writer) error {
+	responseStr := fmt.Sprintln("")
 	for _, s := range response.Steps {
-		responseStr += fmt.Sprintf("\t%s... %s %s\n", s.Title, s.State, s.Data.Message)
+		responseStr += fmt.Sprintf("\t%s...\t\t%s\t%s\n", s.Title, s.State, s.Data.Message)
 	}
 
 	fmt.Fprintf(w, "%s", responseStr)
-	time.Sleep(time.Second * 1)
+	time.Sleep(time.Millisecond * 100)
 
 	return nil
 }
-- 
GitLab


From 8ec096477a0e91b8ae911d05fde26b263d69bae7 Mon Sep 17 00:00:00 2001
From: Payam Qorbanpour <payamqorbanpour@gmail.com>
Date: Sun, 30 Oct 2022 15:28:25 +0330
Subject: [PATCH 3/6] Change api call interval

---
 pkg/paas/migration.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pkg/paas/migration.go b/pkg/paas/migration.go
index f449c88..3ac7ec2 100644
--- a/pkg/paas/migration.go
+++ b/pkg/paas/migration.go
@@ -33,7 +33,7 @@ const (
 	yellowColor       = "\033[33m"
 	resetColor        = "\033[0m"
 	bamdad            = "ba1"
-	interval          = 2
+	interval          = 10
 )
 
 type State string
-- 
GitLab


From ad77affb1dc0335c97b7e3862413fefe4c2ee81d Mon Sep 17 00:00:00 2001
From: Payam Qorbanpour <payamqorbanpour@gmail.com>
Date: Sun, 30 Oct 2022 15:32:21 +0330
Subject: [PATCH 4/6] Fix lint

---
 pkg/paas/migration.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pkg/paas/migration.go b/pkg/paas/migration.go
index 3ac7ec2..7d8f0fc 100644
--- a/pkg/paas/migration.go
+++ b/pkg/paas/migration.go
@@ -40,9 +40,9 @@ type State string
 
 const (
 	Pending   State = "Pending"
-	Doing           = "Doing"
-	Completed       = "Completed"
-	Failed          = "Failed"
+	Doing     State = "Doing"
+	Completed State = "Completed"
+	Failed    State = "Failed"
 )
 
 type Request struct {
-- 
GitLab


From 130f19fcec41ed2d2e0b27779cc9eddaf14e2f57 Mon Sep 17 00:00:00 2001
From: Payam Qorbanpour <payamqorbanpour@gmail.com>
Date: Sun, 30 Oct 2022 17:16:36 +0330
Subject: [PATCH 5/6] Fix error handling

---
 pkg/paas/migration.go | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/pkg/paas/migration.go b/pkg/paas/migration.go
index 7d8f0fc..be11e8d 100644
--- a/pkg/paas/migration.go
+++ b/pkg/paas/migration.go
@@ -106,7 +106,11 @@ func NewCmdMigrate(in io.Reader, out, errout io.Writer) *cobra.Command {
 			explainOut := term.NewResponsiveWriter(out)
 			c.SetOutput(explainOut)
 
-			project, _ := getSelectedProject(in, explainOut)
+			project, err := getSelectedProject(in, explainOut)
+			if err != nil {
+				failureOutput(err.Error())
+				return
+			}
 
 			currentRegionName := getCurrentRegion()
 
@@ -260,6 +264,7 @@ func migrate(request Request) error {
 
 	if postResponse.StatusCode != http.StatusCreated {
 		failureOutput(fmt.Sprint(postResponse.StatusCode))
+		return errors.New(fmt.Sprint(postResponse.StatusCode))
 	}
 
 	// init writer to update lines
@@ -402,7 +407,7 @@ func httpGet(endpoint string) (*ProgressResponse, error) {
 
 // failureOutput displays failure output.
 func failureOutput(message string) {
-	fmt.Println("failed to migrate", message)
+	fmt.Println("failed:", message)
 }
 
 // successOutput displays success output.
-- 
GitLab


From f5e17017665419f527ff127d95cea89ae699a152 Mon Sep 17 00:00:00 2001
From: Payam Qorbanpour <payamqorbanpour@gmail.com>
Date: Sun, 30 Oct 2022 19:32:56 +0330
Subject: [PATCH 6/6] Add ticker function run immidiately

---
 pkg/paas/migration.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pkg/paas/migration.go b/pkg/paas/migration.go
index be11e8d..63fa52f 100644
--- a/pkg/paas/migration.go
+++ b/pkg/paas/migration.go
@@ -303,9 +303,10 @@ func doEvery(d time.Duration, stopChannel chan bool, f func()) {
 	ticker := time.NewTicker(d)
 
 	for {
+		f()
 		select {
 		case <-ticker.C:
-			f()
+			continue
 		case <-stopChannel:
 			ticker.Stop()
 			return
-- 
GitLab