Skip to content

Commit fc56950

Browse files
committed
fix: support non-interactive template uploads
1 parent efbb895 commit fc56950

2 files changed

Lines changed: 129 additions & 9 deletions

File tree

‎cmd/fileupload.go‎

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,33 @@ func UploadFiles(r *Request, api string, response map[string]interface{}, validF
110110
metadata, _ := params["metadata"].(string)
111111

112112
fmt.Println("Uploading files for", api, ":", validFiles)
113-
spinner := r.Config.StartSpinner(uploadingMessage)
113+
114+
var spinner *spinner.Spinner
115+
if r.Config.HasShell {
116+
spinner = r.Config.StartSpinner(uploadingMessage)
117+
}
118+
114119
errored := 0
115120
for i, filePath := range validFiles {
116-
spinner.Suffix = fmt.Sprintf(" uploading %d/%d %s...", i+1, len(validFiles), filepath.Base(filePath))
121+
if spinner != nil {
122+
spinner.Suffix = fmt.Sprintf(" uploading %d/%d %s...", i+1, len(validFiles), filepath.Base(filePath))
123+
}
124+
117125
if err := uploadFile(i, len(validFiles), postURL, filePath, signature, expires, metadata, spinner); err != nil {
118-
spinner.Stop()
126+
if spinner != nil {
127+
spinner.Stop()
128+
}
129+
119130
fmt.Println("Error uploading", filePath, ":", err)
120131
errored++
121-
spinner.Suffix = fmt.Sprintf(" %s", uploadingMessage)
122-
spinner.Start()
132+
133+
if spinner != nil {
134+
spinner.Suffix = fmt.Sprintf(" %s", uploadingMessage)
135+
spinner.Start()
136+
}
123137
}
124138
}
139+
125140
r.Config.StopSpinner(spinner)
126141
if errored > 0 {
127142
fmt.Printf("🙈 %d out of %d files failed to upload.\n", errored, len(validFiles))
@@ -220,7 +235,9 @@ func uploadFile(index, count int, postURL, filePath, signature, expires, metadat
220235
f: tmp,
221236
total: size,
222237
update: func(pct int) {
223-
spn.Suffix = fmt.Sprintf(" [%d/%d] %s\t%s %d%%", index+1, count, fileName, barArrow(pct), pct)
238+
if spn != nil {
239+
spn.Suffix = fmt.Sprintf(" [%d/%d] %s\t%s %d%%", index+1, count, fileName, barArrow(pct), pct)
240+
}
224241
},
225242
}
226243
req.Body = pb
@@ -247,9 +264,13 @@ func uploadFile(index, count int, postURL, filePath, signature, expires, metadat
247264
return fmt.Errorf("[%d/%d] %s\tupload failed: %s", index+1, count, fileName, string(b))
248265
}
249266

250-
spn.Stop()
267+
if spn != nil {
268+
spn.Stop()
269+
}
251270
fmt.Printf("[%d/%d] %s\t%s ✅\n", index+1, count, fileName, barArrow(100))
252-
spn.Suffix = fmt.Sprintf(" %s", uploadingMessage)
253-
spn.Start()
271+
if spn != nil {
272+
spn.Suffix = fmt.Sprintf(" %s", uploadingMessage)
273+
spn.Start()
274+
}
254275
return nil
255276
}

‎cmd/fileupload_test.go‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package cmd
19+
20+
import (
21+
"io"
22+
"net/http"
23+
"net/http/httptest"
24+
"os"
25+
"testing"
26+
27+
"github.com/apache/cloudstack-cloudmonkey/config"
28+
)
29+
30+
func TestUploadFilesNonInteractive(t *testing.T) {
31+
file, err := os.CreateTemp("", "cloudmonkey-upload-test-*")
32+
if err != nil {
33+
t.Fatal(err)
34+
}
35+
defer os.Remove(file.Name())
36+
37+
const fileContent = "test upload content"
38+
39+
if _, err := file.WriteString(fileContent); err != nil {
40+
t.Fatal(err)
41+
}
42+
if err := file.Close(); err != nil {
43+
t.Fatal(err)
44+
}
45+
46+
var uploaded bool
47+
48+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
49+
if r.Method != http.MethodPost {
50+
t.Errorf("request method = %s, want POST", r.Method)
51+
}
52+
53+
if err := r.ParseMultipartForm(10 << 20); err != nil {
54+
t.Fatalf("failed to parse multipart form: %v", err)
55+
}
56+
57+
uploadedFile, _, err := r.FormFile("file")
58+
if err != nil {
59+
t.Fatalf("failed to get uploaded file: %v", err)
60+
}
61+
defer uploadedFile.Close()
62+
63+
data, err := io.ReadAll(uploadedFile)
64+
if err != nil {
65+
t.Fatalf("failed to read uploaded file: %v", err)
66+
}
67+
68+
if string(data) != fileContent {
69+
t.Errorf("uploaded content = %q, want %q", string(data), fileContent)
70+
}
71+
72+
uploaded = true
73+
w.WriteHeader(http.StatusOK)
74+
}))
75+
defer server.Close()
76+
77+
cfg := &config.Config{
78+
HasShell: false,
79+
}
80+
81+
request := &Request{
82+
Config: cfg,
83+
}
84+
85+
response := map[string]interface{}{
86+
"getuploadparams": map[string]interface{}{
87+
"postURL": server.URL,
88+
"metadata": "metadata",
89+
"signature": "signature",
90+
"expires": "expires",
91+
},
92+
}
93+
94+
UploadFiles(request, "getUploadParamsForTemplate", response, []string{file.Name()})
95+
96+
if !uploaded {
97+
t.Fatal("file was not uploaded")
98+
}
99+
}

0 commit comments

Comments
 (0)