@@ -21,6 +21,11 @@ Prefix guest filenames with the instance name and a colon.
21
21
Example: limactl copy default:/etc/os-release .
22
22
`
23
23
24
+ type copyTool string
25
+
26
+ const Rsync copyTool = "rsync"
27
+ const Scp copyTool = "scp"
28
+
24
29
func newCopyCommand () * cobra.Command {
25
30
copyCommand := & cobra.Command {
26
31
Use : "copy SOURCE ... TARGET" ,
@@ -49,13 +54,19 @@ func copyAction(cmd *cobra.Command, args []string) error {
49
54
return err
50
55
}
51
56
52
- arg0 , err := exec .LookPath ("scp" )
57
+ defaultTool := Rsync
58
+ arg0 , err := exec .LookPath (string (defaultTool ))
53
59
if err != nil {
54
- return err
60
+ defaultTool = Scp
61
+ arg0 , err = exec .LookPath (string (defaultTool ))
62
+ if err != nil {
63
+ return err
64
+ }
55
65
}
66
+
56
67
instances := make (map [string ]* store.Instance )
57
- scpFlags := []string {}
58
- scpArgs := []string {}
68
+ copyToolFlags := []string {}
69
+ copyToolArgs := []string {}
59
70
debug , err := cmd .Flags ().GetBool ("debug" )
60
71
if err != nil {
61
72
return err
@@ -65,22 +76,28 @@ func copyAction(cmd *cobra.Command, args []string) error {
65
76
verbose = true
66
77
}
67
78
79
+ useRsync := isCopyToolRsync (defaultTool )
80
+
68
81
if verbose {
69
- scpFlags = append (scpFlags , "-v" )
70
- } else {
71
- scpFlags = append (scpFlags , "-q" )
82
+ copyToolFlags = append (copyToolFlags , "-v" )
83
+ if useRsync {
84
+ copyToolFlags = append (copyToolFlags , "--progress" )
85
+ }
86
+ }
87
+ if ! verbose {
88
+ copyToolFlags = append (copyToolFlags , "-q" )
72
89
}
73
90
74
91
if recursive {
75
- scpFlags = append (scpFlags , "-r" )
92
+ copyToolFlags = append (copyToolFlags , "-r" )
76
93
}
77
94
// this assumes that ssh and scp come from the same place, but scp has no -V
78
95
legacySSH := sshutil .DetectOpenSSHVersion ("ssh" ).LessThan (* semver .New ("8.0.0" ))
79
96
for _ , arg := range args {
80
97
path := strings .Split (arg , ":" )
81
98
switch len (path ) {
82
99
case 1 :
83
- scpArgs = append (scpArgs , arg )
100
+ copyToolArgs = append (copyToolArgs , arg )
84
101
case 2 :
85
102
instName := path [0 ]
86
103
inst , err := store .Inspect (instName )
@@ -93,11 +110,15 @@ func copyAction(cmd *cobra.Command, args []string) error {
93
110
if inst .Status == store .StatusStopped {
94
111
return fmt .Errorf ("instance %q is stopped, run `limactl start %s` to start the instance" , instName , instName )
95
112
}
96
- if legacySSH {
97
- scpFlags = append (scpFlags , "-P" , fmt .Sprintf ("%d" , inst .SSHLocalPort ))
98
- scpArgs = append (
scpArgs ,
fmt .
Sprintf (
"%[email protected] :%s" ,
* inst .
Config .
User .
Name ,
path [
1 ]))
113
+ if useRsync {
114
+ copyToolArgs = append (
copyToolArgs ,
fmt .
Sprintf (
"%[email protected] :%s" ,
* inst .
Config .
User .
Name ,
path [
1 ]))
99
115
} else {
100
- scpArgs = append (
scpArgs ,
fmt .
Sprintf (
"scp://%[email protected] :%d/%s" ,
* inst .
Config .
User .
Name ,
inst .
SSHLocalPort ,
path [
1 ]))
116
+ if legacySSH {
117
+ copyToolFlags = append (copyToolFlags , "-P" , fmt .Sprintf ("%d" , inst .SSHLocalPort ))
118
+ copyToolArgs = append (
copyToolArgs ,
fmt .
Sprintf (
"%[email protected] :%s" ,
* inst .
Config .
User .
Name ,
path [
1 ]))
119
+ } else {
120
+ copyToolArgs = append (
copyToolArgs ,
fmt .
Sprintf (
"scp://%[email protected] :%d/%s" ,
* inst .
Config .
User .
Name ,
inst .
SSHLocalPort ,
path [
1 ]))
121
+ }
101
122
}
102
123
instances [instName ] = inst
103
124
default :
@@ -107,8 +128,10 @@ func copyAction(cmd *cobra.Command, args []string) error {
107
128
if legacySSH && len (instances ) > 1 {
108
129
return errors .New ("more than one (instance) host is involved in this command, this is only supported for openSSH v8.0 or higher" )
109
130
}
110
- scpFlags = append (scpFlags , "-3" , "--" )
111
- scpArgs = append (scpFlags , scpArgs ... )
131
+ if ! useRsync {
132
+ copyToolFlags = append (copyToolFlags , "-3" , "--" )
133
+ }
134
+ copyToolArgs = append (copyToolFlags , copyToolArgs ... )
112
135
113
136
var sshOpts []string
114
137
if len (instances ) == 1 {
@@ -128,14 +151,28 @@ func copyAction(cmd *cobra.Command, args []string) error {
128
151
return err
129
152
}
130
153
}
154
+
131
155
sshArgs := sshutil .SSHArgsFromOpts (sshOpts )
132
156
133
- sshCmd := exec .Command (arg0 , append (sshArgs , scpArgs ... )... )
157
+ sshCmd := exec .Command (arg0 , createArgs (sshArgs , copyToolArgs , defaultTool )... )
134
158
sshCmd .Stdin = cmd .InOrStdin ()
135
159
sshCmd .Stdout = cmd .OutOrStdout ()
136
160
sshCmd .Stderr = cmd .ErrOrStderr ()
137
- logrus .Debugf ("executing scp (may take a long time): %+v" , sshCmd .Args )
161
+ logrus .Debugf ("executing %s (may take a long time): %+v" , arg0 , sshCmd .Args )
138
162
139
163
// TODO: use syscall.Exec directly (results in losing tty?)
140
164
return sshCmd .Run ()
141
165
}
166
+
167
+ func isCopyToolRsync (copyTool copyTool ) bool {
168
+ return copyTool == Rsync
169
+ }
170
+
171
+ func createArgs (sshArgs , copyToolArgs []string , copyTool copyTool ) []string {
172
+ if isCopyToolRsync (copyTool ) {
173
+ rsyncFlags := []string {"-e" , fmt .Sprintf ("ssh %s" , strings .Join (sshArgs , " " ))}
174
+ return append (rsyncFlags , copyToolArgs ... )
175
+ }
176
+
177
+ return append (sshArgs , copyToolArgs ... )
178
+ }
0 commit comments