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