7
7
"encoding/json"
8
8
"fmt"
9
9
"io"
10
+ "io/fs"
11
+ "math"
10
12
"net"
11
13
"net/http"
12
14
"net/url"
@@ -1289,6 +1291,7 @@ type machinesPartCopyFilesArgs struct {
1289
1291
Part string
1290
1292
Recursive bool
1291
1293
Preserve bool
1294
+ NoProgress bool
1292
1295
}
1293
1296
1294
1297
// MachinesPartCopyFilesAction is the corresponding Action for 'machines part cp'.
@@ -1395,6 +1398,7 @@ func (c *viamClient) machinesPartCopyFilesAction(
1395
1398
paths ,
1396
1399
destination ,
1397
1400
logger ,
1401
+ flagArgs .NoProgress ,
1398
1402
)
1399
1403
}
1400
1404
if err := doCopy (); err != nil {
@@ -2471,12 +2475,13 @@ func (c *viamClient) copyFilesToMachine(
2471
2475
paths []string ,
2472
2476
destination string ,
2473
2477
logger logging.Logger ,
2478
+ noProgress bool ,
2474
2479
) error {
2475
2480
shellSvc , closeClient , err := c .connectToShellService (orgStr , locStr , robotStr , partStr , debug , logger )
2476
2481
if err != nil {
2477
2482
return err
2478
2483
}
2479
- return c .copyFilesToMachineInner (shellSvc , closeClient , allowRecursion , preserve , paths , destination )
2484
+ return c .copyFilesToMachineInner (shellSvc , closeClient , allowRecursion , preserve , paths , destination , noProgress )
2480
2485
}
2481
2486
2482
2487
// copyFilesToFqdn is a copyFilesToMachine variant that makes use of pre-fetched part FQDN.
@@ -2488,12 +2493,13 @@ func (c *viamClient) copyFilesToFqdn(
2488
2493
paths []string ,
2489
2494
destination string ,
2490
2495
logger logging.Logger ,
2496
+ noProgress bool ,
2491
2497
) error {
2492
2498
shellSvc , closeClient , err := c .connectToShellServiceFqdn (fqdn , debug , logger )
2493
2499
if err != nil {
2494
2500
return err
2495
2501
}
2496
- return c .copyFilesToMachineInner (shellSvc , closeClient , allowRecursion , preserve , paths , destination )
2502
+ return c .copyFilesToMachineInner (shellSvc , closeClient , allowRecursion , preserve , paths , destination , noProgress )
2497
2503
}
2498
2504
2499
2505
// copyFilesToMachineInner is the common logic for both copyFiles variants.
@@ -2504,16 +2510,81 @@ func (c *viamClient) copyFilesToMachineInner(
2504
2510
preserve bool ,
2505
2511
paths []string ,
2506
2512
destination string ,
2513
+ noProgress bool ,
2507
2514
) error {
2508
2515
defer func () {
2509
2516
utils .UncheckedError (closeClient (c .c .Context ))
2510
2517
}()
2511
2518
2512
- // prepare a factory that understands the file copying service (RPC or not).
2513
- copyFactory := shell .NewCopyFileToMachineFactory (destination , preserve , shellSvc )
2514
- // make a reader copier that just does the traversal and copy work for us. Think of
2515
- // this as a tee reader.
2516
- readCopier , err := shell .NewLocalFileReadCopier (paths , allowRecursion , false , copyFactory )
2519
+ if noProgress {
2520
+ // prepare a factory that understands the file copying service (RPC or not).
2521
+ copyFactory := shell .NewCopyFileToMachineFactory (destination , preserve , shellSvc )
2522
+ // make a reader copier that just does the traversal and copy work for us. Think of
2523
+ // this as a tee reader.
2524
+ readCopier , err := shell .NewLocalFileReadCopier (paths , allowRecursion , false , copyFactory )
2525
+ if err != nil {
2526
+ return err
2527
+ }
2528
+ defer func () {
2529
+ if err := readCopier .Close (c .c .Context ); err != nil {
2530
+ utils .UncheckedError (err )
2531
+ }
2532
+ }()
2533
+
2534
+ // ReadAll the files into the copier.
2535
+ return readCopier .ReadAll (c .c .Context )
2536
+ }
2537
+
2538
+ // Calculate total size of all files to be copied
2539
+ var totalSize int64
2540
+ for _ , path := range paths {
2541
+ info , err := os .Stat (path )
2542
+ if err != nil {
2543
+ return err
2544
+ }
2545
+ if info .IsDir () && allowRecursion {
2546
+ err := filepath .Walk (path , func (path string , info os.FileInfo , err error ) error {
2547
+ if err != nil {
2548
+ return err
2549
+ }
2550
+ if ! info .IsDir () {
2551
+ totalSize += info .Size ()
2552
+ }
2553
+ return nil
2554
+ })
2555
+ if err != nil {
2556
+ return err
2557
+ }
2558
+ } else if ! info .IsDir () {
2559
+ totalSize += info .Size ()
2560
+ }
2561
+ }
2562
+
2563
+ // Create a progress tracking function
2564
+ var currentFile string
2565
+ progressFunc := func (bytes int64 , file string , fileSize int64 ) {
2566
+ if file != currentFile {
2567
+ if currentFile != "" {
2568
+ //nolint:errcheck // progress display is non-critical
2569
+ _ , _ = os .Stdout .WriteString ("\n " )
2570
+ }
2571
+ currentFile = file
2572
+ //nolint:errcheck // progress display is non-critical
2573
+ _ , _ = os .Stdout .WriteString (fmt .Sprintf ("Copying %s...\n " , file ))
2574
+ }
2575
+ uploadPercent := int (math .Ceil (100 * float64 (bytes ) / float64 (fileSize )))
2576
+ //nolint:errcheck // progress display is non-critical
2577
+ _ , _ = os .Stdout .WriteString (fmt .Sprintf ("\r Progress: %d%% (%d/%d bytes)" , uploadPercent , bytes , fileSize ))
2578
+ }
2579
+
2580
+ // Wrap the copy factory to track progress
2581
+ progressFactory := & progressTrackingFactory {
2582
+ factory : shell .NewCopyFileToMachineFactory (destination , preserve , shellSvc ),
2583
+ onProgress : progressFunc ,
2584
+ }
2585
+
2586
+ // Create a new read copier with the progress tracking factory
2587
+ readCopier , err := shell .NewLocalFileReadCopier (paths , allowRecursion , false , progressFactory )
2517
2588
if err != nil {
2518
2589
return err
2519
2590
}
@@ -2524,7 +2595,88 @@ func (c *viamClient) copyFilesToMachineInner(
2524
2595
}()
2525
2596
2526
2597
// ReadAll the files into the copier.
2527
- return readCopier .ReadAll (c .c .Context )
2598
+ err = readCopier .ReadAll (c .c .Context )
2599
+ return err
2600
+ }
2601
+
2602
+ // progressTrackingFactory wraps a copy factory to track progress.
2603
+ type progressTrackingFactory struct {
2604
+ factory shell.FileCopyFactory
2605
+ onProgress func (int64 , string , int64 )
2606
+ }
2607
+
2608
+ func (ptf * progressTrackingFactory ) MakeFileCopier (ctx context.Context , sourceType shell.CopyFilesSourceType ) (shell.FileCopier , error ) {
2609
+ copier , err := ptf .factory .MakeFileCopier (ctx , sourceType )
2610
+ if err != nil {
2611
+ return nil , err
2612
+ }
2613
+ return & progressTrackingCopier {
2614
+ copier : copier ,
2615
+ onProgress : ptf .onProgress ,
2616
+ }, nil
2617
+ }
2618
+
2619
+ // progressTrackingCopier wraps a file copier to track progress.
2620
+ type progressTrackingCopier struct {
2621
+ copier shell.FileCopier
2622
+ onProgress func (int64 , string , int64 )
2623
+ }
2624
+
2625
+ func (ptc * progressTrackingCopier ) Copy (ctx context.Context , file shell.File ) error {
2626
+ // Get file size
2627
+ info , err := file .Data .Stat ()
2628
+ if err != nil {
2629
+ return err
2630
+ }
2631
+ fileSize := info .Size ()
2632
+
2633
+ // Create a progress tracking reader
2634
+ progressReader := & progressReader {
2635
+ reader : file .Data ,
2636
+ onProgress : ptc .onProgress ,
2637
+ fileName : file .RelativeName ,
2638
+ fileSize : fileSize ,
2639
+ }
2640
+
2641
+ // Create a new file with the progress tracking reader
2642
+ progressFile := shell.File {
2643
+ RelativeName : file .RelativeName ,
2644
+ Data : progressReader ,
2645
+ }
2646
+
2647
+ return ptc .copier .Copy (ctx , progressFile )
2648
+ }
2649
+
2650
+ func (ptc * progressTrackingCopier ) Close (ctx context.Context ) error {
2651
+ //nolint:errcheck // progress display is non-critical
2652
+ _ , _ = os .Stdout .WriteString ("\n " )
2653
+ return ptc .copier .Close (ctx )
2654
+ }
2655
+
2656
+ // progressReader wraps a reader to track progress.
2657
+ type progressReader struct {
2658
+ reader fs.File
2659
+ onProgress func (int64 , string , int64 )
2660
+ copied int64
2661
+ fileName string
2662
+ fileSize int64
2663
+ }
2664
+
2665
+ func (pr * progressReader ) Read (p []byte ) (int , error ) {
2666
+ n , err := pr .reader .Read (p )
2667
+ if n > 0 {
2668
+ pr .copied += int64 (n )
2669
+ pr .onProgress (pr .copied , pr .fileName , pr .fileSize )
2670
+ }
2671
+ return n , err
2672
+ }
2673
+
2674
+ func (pr * progressReader ) Stat () (fs.FileInfo , error ) {
2675
+ return pr .reader .Stat ()
2676
+ }
2677
+
2678
+ func (pr * progressReader ) Close () error {
2679
+ return pr .reader .Close ()
2528
2680
}
2529
2681
2530
2682
func (c * viamClient ) copyFilesFromMachine (
0 commit comments