-
Notifications
You must be signed in to change notification settings - Fork 4
Harden deploy migration flow during local and Azure deploys #148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
644a8e8
0735ac2
f289fcd
bbbc087
63a85e0
edb7da9
4b4825f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/DevExpGBB/gh-devlake/internal/devlake" | ||
| ) | ||
|
|
||
| func TestTriggerAndWaitForMigrationWithClient_CompletesAfterTriggerTimeout(t *testing.T) { | ||
| triggerCalls := 0 | ||
| pingCalls := 0 | ||
|
|
||
| srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| switch r.URL.Path { | ||
| case "/proceed-db-migration": | ||
| triggerCalls++ | ||
| time.Sleep(25 * time.Millisecond) | ||
| w.WriteHeader(http.StatusOK) | ||
| case "/ping": | ||
| pingCalls++ | ||
| if pingCalls == 1 { | ||
| w.WriteHeader(http.StatusPreconditionRequired) | ||
| return | ||
| } | ||
| w.WriteHeader(http.StatusOK) | ||
| default: | ||
| http.NotFound(w, r) | ||
| } | ||
| })) | ||
| defer srv.Close() | ||
|
|
||
| client := &devlake.Client{ | ||
| BaseURL: srv.URL, | ||
| HTTPClient: &http.Client{ | ||
| Timeout: 5 * time.Millisecond, | ||
| }, | ||
| } | ||
|
|
||
| err := triggerAndWaitForMigrationWithClient(srv.URL, client, 1, time.Millisecond, 3, time.Millisecond) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
| if triggerCalls != 1 { | ||
| t.Fatalf("trigger calls = %d, want 1", triggerCalls) | ||
| } | ||
| if pingCalls != 2 { | ||
| t.Fatalf("ping calls = %d, want 2", pingCalls) | ||
| } | ||
| } | ||
|
|
||
| func TestTriggerAndWaitForMigrationWithClient_RetriesBeforeWaiting(t *testing.T) { | ||
| triggerCalls := 0 | ||
| pingCalls := 0 | ||
|
|
||
| srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| switch r.URL.Path { | ||
| case "/proceed-db-migration": | ||
| triggerCalls++ | ||
| if triggerCalls == 1 { | ||
| w.WriteHeader(http.StatusServiceUnavailable) | ||
| return | ||
| } | ||
| w.WriteHeader(http.StatusOK) | ||
| case "/ping": | ||
| pingCalls++ | ||
| w.WriteHeader(http.StatusOK) | ||
| default: | ||
| http.NotFound(w, r) | ||
| } | ||
| })) | ||
| defer srv.Close() | ||
|
|
||
| client := devlake.NewClient(srv.URL) | ||
|
|
||
| err := triggerAndWaitForMigrationWithClient(srv.URL, client, 2, time.Millisecond, 2, time.Millisecond) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
| if triggerCalls != 2 { | ||
| t.Fatalf("trigger calls = %d, want 2", triggerCalls) | ||
| } | ||
| if pingCalls != 1 { | ||
| t.Fatalf("ping calls = %d, want 1", pingCalls) | ||
| } | ||
| } | ||
|
Comment on lines
+54
to
+88
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,16 @@ | ||
| // Package devlake provides an HTTP client for the DevLake REST API. | ||
| package devlake | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "encoding/json" | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| "net/url" | ||
| "time" | ||
| ) | ||
| import ( | ||
| "bytes" | ||
| "encoding/json" | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| "net/url" | ||
| "strings" | ||
| "time" | ||
| ) | ||
|
|
||
| // Client wraps HTTP calls to the DevLake backend API. | ||
| type Client struct { | ||
|
|
@@ -506,12 +507,20 @@ func (c *Client) SearchRemoteScopes(plugin string, connID int, search string, pa | |
| // TriggerMigration triggers the DevLake database migration endpoint. | ||
| func (c *Client) TriggerMigration() error { | ||
| resp, err := c.HTTPClient.Get(c.BaseURL + "/proceed-db-migration") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| resp.Body.Close() | ||
| return nil | ||
| } | ||
| if err != nil { | ||
| return fmt.Errorf("triggering migration: %w", err) | ||
| } | ||
| defer resp.Body.Close() | ||
| if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { | ||
| body, _ := io.ReadAll(io.LimitReader(resp.Body, 512)) | ||
| bodyText := strings.TrimSpace(string(body)) | ||
| if bodyText != "" { | ||
| return fmt.Errorf("DevLake returned status %d: %s", resp.StatusCode, bodyText) | ||
| } | ||
| return fmt.Errorf("DevLake returned status %d", resp.StatusCode) | ||
| } | ||
|
Comment on lines
507
to
+521
|
||
| return nil | ||
| } | ||
|
|
||
| // PipelineListResponse is the response from GET /pipelines. | ||
| type PipelineListResponse struct { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
triggerAndWaitForMigrationWithClienttakes bothbaseURLand adevlakeClientthat already carries aBaseURL. If these ever diverge, migration trigger and migration wait will hit different instances. Consider deriving the wait URL fromdevlakeClient.BaseURL(or validating they match) to avoid accidental mismatches.This issue also appears on line 256 of the same file.