detached 1186987

fix: rewrite upload image logic client and server side to avoid timeouts

commit
1186987bb2fe420e324ef0c5032a06303b4581fb
author
bnema · · 2024-04-21 01:40 UTC
parent
0e99910
signature
unsigned
2 files · +47 -30
M internal/cli/cmd/deploy.go +25 -22
@@ -7,6 +7,7 @@ import (
77 "net/http"
88 "os"
99 "strings"
10+ "sync"
1011 "time"
1112
1213 "github.com/bnema/gordon/internal/cli"
@@ -85,7 +86,11 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
8586 },
8687 }
8788
89+ var wg sync.WaitGroup
90+ wg.Add(1)
91+
8892 go func() {
93+ defer wg.Done()
8994 resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push")
9095 if err != nil {
9196 errCh <- fmt.Errorf("error sending HTTP request: %w", err)
@@ -136,32 +141,30 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
136141 errCh <- fmt.Errorf("error running progress bar TUI: %w", err)
137142 return
138143 }
139- done := false
140-
141- for !done {
142- select {
143- case err := <-errCh:
144- if err != nil {
145- fmt.Println(err)
146- return
147- }
148- case <-time.After(1 * time.Second):
149- // Check if the progress bar is done
150- if m.Done {
151- done = true
152- }
144+ // Wait for the deployment goroutine to complete
145+ wg.Wait()
146+
147+ // Check for errors from the deployment goroutine
148+ select {
149+ case err := <-errCh:
150+ if err != nil {
151+ fmt.Println(err)
152+ return
153153 }
154+ default:
154155 }
155156
156- // Close the reader
157- err = progressReader.Close()
158- if err != nil {
159- fmt.Println("Error closing reader:", err)
160- return
157+ // Check if the progress bar is done
158+ if m.Done {
159+ // Close the reader
160+ err = progressReader.Close()
161+ if err != nil {
162+ fmt.Println("Error closing reader:", err)
163+ return
164+ }
165+ } else {
166+ fmt.Println("Deployment completed, but progress bar is not done.")
161167 }
162-
163- m.Done = true
164-
165168 },
166169 }
167170
@@ -7,6 +7,7 @@ import (
7 "net/http"7 "net/http"
8 "os"8 "os"
9 "strings"9 "strings"
10+ "sync"
10 "time"11 "time"
11 12
12 "github.com/bnema/gordon/internal/cli"13 "github.com/bnema/gordon/internal/cli"
@@ -85,7 +86,11 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
85 },86 },
86 }87 }
87 88
89+ var wg sync.WaitGroup
90+ wg.Add(1)
91+
88 go func() {92 go func() {
93+ defer wg.Done()
89 resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push")94 resp, err := handler.SendHTTPRequest(a, &reqPayload, "POST", "/push")
90 if err != nil {95 if err != nil {
91 errCh <- fmt.Errorf("error sending HTTP request: %w", err)96 errCh <- fmt.Errorf("error sending HTTP request: %w", err)
@@ -136,32 +141,30 @@ func NewDeployCommand(a *cli.App) *cobra.Command {
136 errCh <- fmt.Errorf("error running progress bar TUI: %w", err)141 errCh <- fmt.Errorf("error running progress bar TUI: %w", err)
137 return142 return
138 }143 }
139- done := false144+ // Wait for the deployment goroutine to complete
140-145+ wg.Wait()
141- for !done {146+
142- select {147+ // Check for errors from the deployment goroutine
143- case err := <-errCh:148+ select {
144- if err != nil {149+ case err := <-errCh:
145- fmt.Println(err)150+ if err != nil {
146- return151+ fmt.Println(err)
147- }152+ return
148- case <-time.After(1 * time.Second):
149- // Check if the progress bar is done
150- if m.Done {
151- done = true
152- }
153 }153 }
154+ default:
154 }155 }
155 156
156- // Close the reader157+ // Check if the progress bar is done
157- err = progressReader.Close()158+ if m.Done {
158- if err != nil {159+ // Close the reader
159- fmt.Println("Error closing reader:", err)160+ err = progressReader.Close()
160- return161+ if err != nil {
162+ fmt.Println("Error closing reader:", err)
163+ return
164+ }
165+ } else {
166+ fmt.Println("Deployment completed, but progress bar is not done.")
161 }167 }
162-
163- m.Done = true
164-
165 },168 },
166 }169 }
167 170
M internal/httpserve/handler/uploadcontainerimage.go +22 -8
@@ -1,10 +1,10 @@
11 package handler
22
33 import (
4- "bytes"
54 "fmt"
65 "io"
76 "net/http"
7+ "os"
88
99 "github.com/bnema/gordon/internal/server"
1010 "github.com/bnema/gordon/internal/templating/render"
@@ -13,7 +13,8 @@ import (
1313 "github.com/labstack/echo/v4"
1414 )
1515
16-const MaxUploadSize = 1024 * 1024 * 1024 // 1GB
16+// 10G
17+const MaxUploadSize = 10 * 1024 * 1024 * 1024 // 10GB
1718
1819 // UploadImageHandler handles the /upload-image route to show the form
1920 func UploadImageGETHandler(c echo.Context, a *server.App) error {
@@ -46,19 +47,32 @@ func UploadImagePOSTHandler(c echo.Context, a *server.App) error {
4647 if err != nil {
4748 return sendError(c, err)
4849 }
50+ defer file.Close()
4951
50- // Get the file size
51- buf := new(bytes.Buffer)
52- _, err = io.Copy(buf, file)
52+ // Get the filename
53+ filename := header.Filename
54+
55+ // Create a temporary file to store the uploaded chunks
56+ tempFile, err := os.CreateTemp("", "upload-*")
5357 if err != nil {
5458 return sendError(c, err)
5559 }
60+ defer tempFile.Close()
5661
57- // Get the filename
58- filename := header.Filename
62+ // Copy the file chunks to the temporary file
63+ _, err = io.Copy(tempFile, file)
64+ if err != nil {
65+ return sendError(c, err)
66+ }
67+
68+ // Reset the temporary file pointer
69+ _, err = tempFile.Seek(0, io.SeekStart)
70+ if err != nil {
71+ return sendError(c, err)
72+ }
5973
6074 // Save the image to the storage directory
61- saveInPath, err := store.SaveImageToStorage(&a.Config, filename, buf)
75+ saveInPath, err := store.SaveImageToStorage(&a.Config, filename, tempFile)
6276 if err != nil {
6377 return sendError(c, err)
6478 }
@@ -1,10 +1,10 @@
1 package handler1 package handler
2 2
3 import (3 import (
4- "bytes"
5 "fmt"4 "fmt"
6 "io"5 "io"
7 "net/http"6 "net/http"
7+ "os"
8 8
9 "github.com/bnema/gordon/internal/server"9 "github.com/bnema/gordon/internal/server"
10 "github.com/bnema/gordon/internal/templating/render"10 "github.com/bnema/gordon/internal/templating/render"
@@ -13,7 +13,8 @@ import (
13 "github.com/labstack/echo/v4"13 "github.com/labstack/echo/v4"
14 )14 )
15 15
16-const MaxUploadSize = 1024 * 1024 * 1024 // 1GB16+// 10G
17+const MaxUploadSize = 10 * 1024 * 1024 * 1024 // 10GB
17 18
18 // UploadImageHandler handles the /upload-image route to show the form19 // UploadImageHandler handles the /upload-image route to show the form
19 func UploadImageGETHandler(c echo.Context, a *server.App) error {20 func UploadImageGETHandler(c echo.Context, a *server.App) error {
@@ -46,19 +47,32 @@ func UploadImagePOSTHandler(c echo.Context, a *server.App) error {
46 if err != nil {47 if err != nil {
47 return sendError(c, err)48 return sendError(c, err)
48 }49 }
50+ defer file.Close()
49 51
50- // Get the file size52+ // Get the filename
51- buf := new(bytes.Buffer)53+ filename := header.Filename
52- _, err = io.Copy(buf, file)54+
55+ // Create a temporary file to store the uploaded chunks
56+ tempFile, err := os.CreateTemp("", "upload-*")
53 if err != nil {57 if err != nil {
54 return sendError(c, err)58 return sendError(c, err)
55 }59 }
60+ defer tempFile.Close()
56 61
57- // Get the filename62+ // Copy the file chunks to the temporary file
58- filename := header.Filename63+ _, err = io.Copy(tempFile, file)
64+ if err != nil {
65+ return sendError(c, err)
66+ }
67+
68+ // Reset the temporary file pointer
69+ _, err = tempFile.Seek(0, io.SeekStart)
70+ if err != nil {
71+ return sendError(c, err)
72+ }
59 73
60 // Save the image to the storage directory74 // Save the image to the storage directory
61- saveInPath, err := store.SaveImageToStorage(&a.Config, filename, buf)75+ saveInPath, err := store.SaveImageToStorage(&a.Config, filename, tempFile)
62 if err != nil {76 if err != nil {
63 return sendError(c, err)77 return sendError(c, err)
64 }78 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close