(#8933) Align file provisioner functionality on all platforms
This commit aligns how the file provisioner should work on all host machines. It ensures that a `/.` is only applied if the user intended to upload a folder to a destination under a different name. It ensures that if uploading to a windows guest with a different destination folder name, it does not nest the source folder under that name so that it works the same as it does on linux platforms. It also updates the behavior of the winrm upload communicator by allowing an array of paths to be uploaded instead of a single file or folder to allow for this new functionality for windows guests.
This commit is contained in:
parent
9a992ffe17
commit
a9564b2137
|
@ -83,9 +83,22 @@ module VagrantPlugins
|
|||
raise_winrm_exception(e, "run_wql", query)
|
||||
end
|
||||
|
||||
# @param from [Array<String>, String] a single path or folder, or an
|
||||
# array of paths and folders to upload to the guest
|
||||
# @param to [String] a path or folder on the guest to upload to
|
||||
# @return [FixNum] Total size transfered from host to guest
|
||||
def upload(from, to)
|
||||
file_manager = WinRM::FS::FileManager.new(connection)
|
||||
file_manager.upload(from, to)
|
||||
if from.is_a?(Array)
|
||||
# Preserve return FixNum of bytes transfered
|
||||
return_bytes = 0
|
||||
from.each do |file|
|
||||
return_bytes += file_manager.upload(file, to)
|
||||
end
|
||||
return return_bytes
|
||||
else
|
||||
file_manager.upload(from, to)
|
||||
end
|
||||
end
|
||||
|
||||
def download(from, to)
|
||||
|
|
|
@ -11,15 +11,28 @@ module VagrantPlugins
|
|||
if File.directory?(source)
|
||||
# We need to make sure the actual destination folder
|
||||
# also exists before uploading, otherwise
|
||||
# you will get nested folders. We also need to append
|
||||
# a './' to the source folder so we copy the contents
|
||||
# rather than the folder itself, in case a users destination
|
||||
# folder differs from its source.
|
||||
# you will get nested folders
|
||||
#
|
||||
# https://serverfault.com/questions/538368/make-scp-always-overwrite-or-create-directory
|
||||
# https://unix.stackexchange.com/questions/292641/get-scp-path-behave-like-rsync-path/292732
|
||||
command = "mkdir -p \"%s\"" % destination
|
||||
source << "/."
|
||||
if !destination.end_with?(File::SEPARATOR) &&
|
||||
!source.end_with?("#{File::SEPARATOR}.")
|
||||
# We also need to append a '/.' to the source folder so we copy
|
||||
# the contents rather than the folder itself, in case a users
|
||||
# destination folder differs from its source
|
||||
#
|
||||
# If source is set as `source/` it will lose the trailing
|
||||
# slash due to how `File.expand_path` works, so we don't need
|
||||
# a conditional for that case.
|
||||
if @machine.config.vm.communicator == :winrm
|
||||
# windows needs an array of paths because of the
|
||||
# winrm-fs function Vagrant is using to upload file/folder.
|
||||
source = Dir["#{source}#{File::SEPARATOR}*"]
|
||||
else
|
||||
source << "#{File::SEPARATOR}."
|
||||
end
|
||||
end
|
||||
else
|
||||
command = "mkdir -p \"%s\"" % File.dirname(destination)
|
||||
end
|
||||
|
|
|
@ -31,6 +31,35 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#upload" do
|
||||
let(:fm) { double("file_manager") }
|
||||
it "should call file_manager.upload for each passed in path" do
|
||||
from = ["/path", "/path/folder", "/path/folder/file.py"]
|
||||
to = "/destination"
|
||||
size = 80
|
||||
|
||||
allow(WinRM::FS::FileManager).to receive(:new).with(connection)
|
||||
.and_return(fm)
|
||||
allow(fm).to receive(:upload).and_return(size)
|
||||
|
||||
expect(fm).to receive(:upload).exactly(from.size).times
|
||||
expect(subject.upload(from, to)).to eq(size*from.size)
|
||||
end
|
||||
|
||||
it "should call file_manager.upload once for a single path" do
|
||||
from = "/path/folder/file.py"
|
||||
to = "/destination"
|
||||
size = 80
|
||||
|
||||
allow(WinRM::FS::FileManager).to receive(:new).with(connection)
|
||||
.and_return(fm)
|
||||
allow(fm).to receive(:upload).and_return(size)
|
||||
|
||||
expect(fm).to receive(:upload).exactly(1).times
|
||||
expect(subject.upload(from, to)).to eq(size)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".powershell" do
|
||||
it "should call winrm powershell" do
|
||||
expect(shell).to receive(:run).with("dir").and_return(output)
|
||||
|
@ -66,7 +95,7 @@ describe VagrantPlugins::CommunicatorWinRM::WinRMShell do
|
|||
end
|
||||
end
|
||||
|
||||
describe ".cmd" do
|
||||
describe ".cmd" do
|
||||
it "should call winrm cmd" do
|
||||
expect(connection).to receive(:shell).with(:cmd, { })
|
||||
expect(shell).to receive(:run).with("dir").and_return(output)
|
||||
|
|
|
@ -89,5 +89,39 @@ describe VagrantPlugins::FileUpload::Provisioner do
|
|||
|
||||
subject.provision
|
||||
end
|
||||
|
||||
it "appends a '/.' if the destination doesnt end with a file separator" do
|
||||
allow(config).to receive(:source).and_return("/source")
|
||||
allow(config).to receive(:destination).and_return("/foo/bar")
|
||||
allow(File).to receive(:directory?).with("/source").and_return(true)
|
||||
|
||||
expect(guest).to receive(:capability?).
|
||||
with(:shell_expand_guest_path).and_return(true)
|
||||
expect(guest).to receive(:capability).
|
||||
with(:shell_expand_guest_path, "/foo/bar").and_return("/foo/bar")
|
||||
|
||||
expect(communicator).to receive(:upload).with("/source/.", "/foo/bar")
|
||||
|
||||
subject.provision
|
||||
end
|
||||
|
||||
it "sends an array of files and folders if winrm and destination doesn't end with file separator" do
|
||||
files = ["/source/file.py", "/source/folder"]
|
||||
allow(Dir).to receive(:[]).and_return(files)
|
||||
allow(config).to receive(:source).and_return("/source")
|
||||
allow(config).to receive(:destination).and_return("/foo/bar")
|
||||
allow(File).to receive(:directory?).with("/source").and_return(true)
|
||||
allow(machine.config.vm).to receive(:communicator).and_return(:winrm)
|
||||
|
||||
expect(guest).to receive(:capability?).
|
||||
with(:shell_expand_guest_path).and_return(true)
|
||||
expect(guest).to receive(:capability).
|
||||
with(:shell_expand_guest_path, "/foo/bar").and_return("/foo/bar")
|
||||
|
||||
expect(communicator).to receive(:upload)
|
||||
.with(files, "/foo/bar")
|
||||
|
||||
subject.provision
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,42 @@ new VM.
|
|||
config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
|
||||
end
|
||||
|
||||
If you want to upload a folder to your guest system, it can be accomplished by
|
||||
using a file provisioner seen below. When copied, the resulting folder on the guest will
|
||||
replace `folder` as `newfolder` and place its on the guest machine. Note that if
|
||||
you'd like the same folder name on your guest machine, make sure that the destination
|
||||
path has the same name as the folder on your host.
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
# ... other configuration
|
||||
|
||||
config.vm.provision "file", source: "~/path/to/host/folder", destination: "$HOME/remote/newfolder"
|
||||
end
|
||||
|
||||
Prior to copying `~/path/to/host/folder` to the guest machine:
|
||||
|
||||
folder
|
||||
├── script.sh
|
||||
├── otherfolder
|
||||
│ └── hello.sh
|
||||
├── goodbye.sh
|
||||
├── hello.sh
|
||||
└── woot.sh
|
||||
|
||||
1 directory, 5 files
|
||||
|
||||
After to copying `~/path/to/host/folder` into `$HOME/remote/newfolder` to the guest machine:
|
||||
|
||||
newfolder
|
||||
├── script.sh
|
||||
├── otherfolder
|
||||
│ └── hello.sh
|
||||
├── goodbye.sh
|
||||
├── hello.sh
|
||||
└── woot.sh
|
||||
|
||||
1 directory, 5 files
|
||||
|
||||
Note that, unlike with synced folders, files or directories that are uploaded
|
||||
will not be kept in sync. Continuing with the example above, if you make
|
||||
further changes to your local ~/.gitconfig, they will not be immediately
|
||||
|
@ -49,3 +85,36 @@ The file provisioner takes only two options, both of which are required:
|
|||
the source will be uploaded to. The file/folder is uploaded as the SSH user
|
||||
over SCP, so this location must be writable to that user. The SSH user can be
|
||||
determined by running `vagrant ssh-config`, and defaults to "vagrant".
|
||||
|
||||
## Caveats
|
||||
|
||||
While the file provisioner does support trailing slashes or "globing", this can
|
||||
lead to some confusing results due to the underlying tool used to copy files and
|
||||
folders between the host and guests. For example, if you have a source and
|
||||
destination with a trailing slash defined below:
|
||||
|
||||
config.vm.provision "file", source: "~/pathfolder", destination: "/remote/newlocation/"
|
||||
|
||||
You are telling vagrant to upload `~/pathfolder` under the remote dir `/remote/newlocation`,
|
||||
which will look like:
|
||||
|
||||
newlocation
|
||||
├── pathfolder
|
||||
│ └── file.sh
|
||||
|
||||
1 directory, 2 files
|
||||
|
||||
This behavior can also be achieved by defining your file provisioner below:
|
||||
|
||||
config.vm.provision "file", source: "~/pathfolder", destination: "/remote/newlocation/pathfolder"
|
||||
|
||||
Another example is using globing on the host machine to grab all files within a
|
||||
folder, but not the top level folder itself:
|
||||
|
||||
config.vm.provision "file", source: "~/otherfolder/.", destination: "/remote/otherlocation"
|
||||
|
||||
The file provisioner is defined to include all files under `~/otherfolder`
|
||||
to the new location `/remote/otherlocation`. This idea can be achieved by simply
|
||||
having your destination folder differ from the source folder:
|
||||
|
||||
config.vm.provision "file", source: "/otherfolder", destination: "/remote/otherlocation"
|
||||
|
|
Loading…
Reference in New Issue