-
Notifications
You must be signed in to change notification settings - Fork 669
Add copy/cp command #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
812dbb1
to
190d1b1
Compare
Thanks, but I heard scp is deprecated https://lists.mindrot.org/pipermail/openssh-unix-dev/2019-March/037672.html Can we use sftp or rsync? |
cmd/limactl/copy.go
Outdated
Name: "copy", | ||
Aliases: []string{"cp", "scp"}, | ||
Usage: "Copy files between host and guest", | ||
Description: "Prefix guest filenames with a colon. Example: limactl copy default :/etc/os-release .", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need colon?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need some mechanism to tell which file is on the host, and which is on the guest. If we don't use the colon, how would you specify the direction:
# copy from guest to host
limactl cp default :/etc/hostname hostname
# copy from host to guest
limactl cp default file1 file2 file3 :.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative syntax: limactl cp default:/etc/hostname hostname
.
This syntax can be also extended to limactl cp inst1:/foo inst2:/foo
to copy files across instances.
This can be also extended to limactl cp inst1 inst2
to copy instances, though it might be confusing.
But given that limactl ls
lists instances and limactl delete
deletes instances, limactl cp inst1 inst2
might be probably acceptable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This syntax can be also extended to
limactl cp inst1:/foo inst2:/foo
to copy files across instances.
I like it. I just did a manual test, and it seems to work:
$ scp -i ~/.lima/_config/user -o NoHostAuthenticationForLocalhost=yes -3 scp://[email protected]:60020//etc/hostname scp://[email protected]:60022/myname
$ limactl shell default sh -c 'cat ~/myname'
lima-alpine
I was confused for a bit about the obscure syntax: the first slash after the port is just a separator and not part of the path. You need a double-slash to specify an absolute path. At least it means relative paths are still supported.
The copy still has to go through the host (using -3
) and not directly between the instances because they use different ports.
I guess theoretically you could use ssh
to run the scp
command from within the source instances, but that seems like unnecessary complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it will be possible to use the ControlPath
when we copy between instances, so this will complicate the logic some more...
I'll take a look into it tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, limactl cp inst1:/foo inst2:/foo
can be implemented later, we just have to determine the CLI syntax here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just implemented it:
$ limactl cp alpine:/etc/os-release opensuse:release-info
$ limactl shell opensuse sh -c 'cat ~/release-info'
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.13.5
PRETTY_NAME="Alpine Linux v3.13"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
scp will not use the ControlPath
, but I think that is fine; it is just an optimization.
It is my understanding that the scp protocol is deprecated, not the cli tool: openssh/openssh-portable#194
I prefer to use |
0a05876
to
0d8ecf1
Compare
pkg/sshutil/sshutil.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
args, err := SCPArgs() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing, can we just use a single function like func SSHArgs(instDir string, useControlMaster bool)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing,
Not sure why this is confusing: the SCP arguments are just a subset of the SSH arguments. There are also potential differences (not relevant here), like -p
for SSH is -P
for SCP because -p
for SCP means "preserve access mode and file times"...
can we just use a single function like func SSHArgs(instDir string, useControlMaster bool) ?
So you would call it with an empty string as the instDir
from the copyCommand
? Because we no longer have a global instDir
, it can be different for each source argument.
Please reconsider! I think the current implementation is less confusing than forcing both commands to use the same function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was confused because "SCPArgs" is called even when we do not use scp.
Just renaming SCPArgs()
to CommonArgs()
and let var SCPArgs = CommonArgs
might be less confusing, but anyway I don't have a strong opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've renamed SCPArgs
to CommonArgs
now.
return err | ||
} | ||
args = append(args, "-3", "--") | ||
for _, arg := range clicontext.Args().Slice() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we will eventually want unit tests for the parser, but can be added in separate PR in future
return err | ||
} | ||
args = append(args, "-3", "--") | ||
for _, arg := range clicontext.Args().Slice() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to limit the length of the slice to be 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? You can specify multiple source arguments when the destination is a directory:
$ limactl cp alpine:/etc/os-release default:/etc/hostname opensuse:.
$ limactl shell opensuse sh -c 'ls -l ~'
total 12
drwxr-xr-x 2 jan users 6 Mar 3 11:52 bin
-rw-r--r-- 1 jan users 13 Jun 29 07:47 hostname
-rw-r--r-- 1 jan users 164 Jun 29 07:47 os-release
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that's awesome 👍
for _, arg := range clicontext.Args().Slice() { | ||
path := strings.Split(arg, ":") | ||
switch len(path) { | ||
case 1: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we require /
to be included in a host file path?
Because we may want limactl cp inst1 inst2
to duplicate an instance in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we require
/
to be included in a host file path?
We could, but it feels slightly wrong to do so. Wouldn't it make more sense to say if none of the arguments contain a colon (i.e. all arguments are host references), then the arguments must be 2 instance names, of which the first one must already exist, and the second one must not?
Because limactl cp inst1 inst2
would otherwise just be a local copy between 2 files, which doesn't need limactl cp
, you can use just plain cp
for that. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I guess we are unlikely going to overload limactl cp
for copying instances.
For copying instances we can just have limactl clone inst1 inst2
or maybe limactl copy-instance inst1 inst2
.
If a user attempted to run limactl cp inst1 inst2
, we can just print a warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I guess we are unlikely going to overload
limactl cp
for copying instances.
For copying instances we can just havelimactl clone inst1 inst2
or maybelimactl copy-instance inst1 inst2
.
Good. I don't really like overloading commands with unrelated functionality, but couldn't think of an "obvious" name for copying an instance. I think clone
is perfect for this.
If a user attempted to run
limactl cp inst1 inst2
, we can just print a warning.
Not sure if we need to warn; limectl cp foo bar
really does the same thing as cp foo bar
right now, and makes sense to me: if source and dest can be both remote (guest), then why shouldn't they both be allowed to be local (host) as well? And if the source file does not exist, we already get a warning:
$ limactl cp foo bar
cp: foo: No such file or directory
exit status 1
This is a convenience wrapper around scp. Example: limactl cp alpine:/etc/os-release default:alpine-release Signed-off-by: Jan Dubois <[email protected]>
This is a wrapper around
scp
. User doesn't have to lookup / specify the port number, and doesn't have to prefix remote names with127.0.0.1:
; a single:
is enough. And the connection goes through the ControlPath.Example: