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") + } +}