This is an automated email from the ASF dual-hosted git repository.

DaanHoogland pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack-cloudmonkey.git


The following commit(s) were added to refs/heads/main by this push:
     new 57e39ce  fix: support non-interactive template uploads (#228)
57e39ce is described below

commit 57e39ce7dc52e9003db690d5a0d610f0bef5c178
Author: Dheeraj Bansal <[email protected]>
AuthorDate: Mon Oct 12 18:22:07 2026 +0530

    fix: support non-interactive template uploads (#228)
---
 cmd/fileupload.go      | 39 +++++++++++++++-----
 cmd/fileupload_test.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 129 insertions(+), 9 deletions(-)

diff --git a/cmd/fileupload.go b/cmd/fileupload.go
index 30bf986..ecd6a06 100644
--- a/cmd/fileupload.go
+++ b/cmd/fileupload.go
@@ -110,18 +110,33 @@ func UploadFiles(r *Request, api string, response 
map[string]interface{}, validF
        metadata, _ := params["metadata"].(string)
 
        fmt.Println("Uploading files for", api, ":", validFiles)
-       spinner := r.Config.StartSpinner(uploadingMessage)
+
+       var spinner *spinner.Spinner
+       if r.Config.HasShell {
+               spinner = r.Config.StartSpinner(uploadingMessage)
+       }
+
        errored := 0
        for i, filePath := range validFiles {
-               spinner.Suffix = fmt.Sprintf(" uploading %d/%d %s...", i+1, 
len(validFiles), filepath.Base(filePath))
+               if spinner != nil {
+                       spinner.Suffix = fmt.Sprintf(" uploading %d/%d %s...", 
i+1, len(validFiles), filepath.Base(filePath))
+               }
+
                if err := uploadFile(i, len(validFiles), postURL, filePath, 
signature, expires, metadata, spinner); err != nil {
-                       spinner.Stop()
+                       if spinner != nil {
+                               spinner.Stop()
+                       }
+
                        fmt.Println("Error uploading", filePath, ":", err)
                        errored++
-                       spinner.Suffix = fmt.Sprintf(" %s", uploadingMessage)
-                       spinner.Start()
+
+                       if spinner != nil {
+                               spinner.Suffix = fmt.Sprintf(" %s", 
uploadingMessage)
+                               spinner.Start()
+                       }
                }
        }
+
        r.Config.StopSpinner(spinner)
        if errored > 0 {
                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
                f:     tmp,
                total: size,
                update: func(pct int) {
-                       spn.Suffix = fmt.Sprintf(" [%d/%d] %s\t%s %d%%", 
index+1, count, fileName, barArrow(pct), pct)
+                       if spn != nil {
+                               spn.Suffix = fmt.Sprintf(" [%d/%d] %s\t%s 
%d%%", index+1, count, fileName, barArrow(pct), pct)
+                       }
                },
        }
        req.Body = pb
@@ -247,9 +264,13 @@ func uploadFile(index, count int, postURL, filePath, 
signature, expires, metadat
                return fmt.Errorf("[%d/%d] %s\tupload failed: %s", index+1, 
count, fileName, string(b))
        }
 
-       spn.Stop()
+       if spn != nil {
+               spn.Stop()
+       }
        fmt.Printf("[%d/%d] %s\t%s ✅\n", index+1, count, fileName, 
barArrow(100))
-       spn.Suffix = fmt.Sprintf(" %s", uploadingMessage)
-       spn.Start()
+       if spn != nil {
+               spn.Suffix = fmt.Sprintf(" %s", uploadingMessage)
+               spn.Start()
+       }
        return nil
 }
diff --git a/cmd/fileupload_test.go b/cmd/fileupload_test.go
new file mode 100644
index 0000000..93b93c8
--- /dev/null
+++ b/cmd/fileupload_test.go
@@ -0,0 +1,99 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package cmd
+
+import (
+       "io"
+       "net/http"
+       "net/http/httptest"
+       "os"
+       "testing"
+
+       "github.com/apache/cloudstack-cloudmonkey/config"
+)
+
+func TestUploadFilesNonInteractive(t *testing.T) {
+       file, err := os.CreateTemp("", "cloudmonkey-upload-test-*")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.Remove(file.Name())
+
+       const fileContent = "test upload content"
+
+       if _, err := file.WriteString(fileContent); err != nil {
+               t.Fatal(err)
+       }
+       if err := file.Close(); err != nil {
+               t.Fatal(err)
+       }
+
+       var uploaded bool
+
+       server := httptest.NewServer(http.HandlerFunc(func(w 
http.ResponseWriter, r *http.Request) {
+               if r.Method != http.MethodPost {
+                       t.Errorf("request method = %s, want POST", r.Method)
+               }
+
+               if err := r.ParseMultipartForm(10 << 20); err != nil {
+                       t.Fatalf("failed to parse multipart form: %v", err)
+               }
+
+               uploadedFile, _, err := r.FormFile("file")
+               if err != nil {
+                       t.Fatalf("failed to get uploaded file: %v", err)
+               }
+               defer uploadedFile.Close()
+
+               data, err := io.ReadAll(uploadedFile)
+               if err != nil {
+                       t.Fatalf("failed to read uploaded file: %v", err)
+               }
+
+               if string(data) != fileContent {
+                       t.Errorf("uploaded content = %q, want %q", 
string(data), fileContent)
+               }
+
+               uploaded = true
+               w.WriteHeader(http.StatusOK)
+       }))
+       defer server.Close()
+
+       cfg := &config.Config{
+               HasShell: false,
+       }
+
+       request := &Request{
+               Config: cfg,
+       }
+
+       response := map[string]interface{}{
+               "getuploadparams": map[string]interface{}{
+                       "postURL":   server.URL,
+                       "metadata":  "metadata",
+                       "signature": "signature",
+                       "expires":   "expires",
+               },
+       }
+
+       UploadFiles(request, "getUploadParamsForTemplate", response, 
[]string{file.Name()})
+
+       if !uploaded {
+               t.Fatal("file was not uploaded")
+       }
+}

Reply via email to