Provides simple nfs service name detection via systemd or sys-v. Defaults are provided if no match is found. Service name is defined via method allowing derivative guests to only need to provide an updated name.
283 lines
10 KiB
Ruby
283 lines
10 KiB
Ruby
require_relative "../../../../base"
|
|
require_relative "../../../../../../plugins/hosts/linux/cap/nfs"
|
|
require_relative "../../../../../../lib/vagrant/util"
|
|
|
|
describe VagrantPlugins::HostLinux::Cap::NFS do
|
|
|
|
include_context "unit"
|
|
|
|
let(:caps) do
|
|
VagrantPlugins::HostLinux::Plugin
|
|
.components
|
|
.host_capabilities[:linux]
|
|
end
|
|
|
|
let(:tmp_exports_path) do
|
|
@tmp_exports ||= temporary_file
|
|
end
|
|
let(:exports_path){ VagrantPlugins::HostLinux::Cap::NFS::NFS_EXPORTS_PATH }
|
|
let(:env){ double(:env) }
|
|
let(:ui){ double(:ui) }
|
|
let(:host){ double(:host) }
|
|
|
|
before do
|
|
@original_exports_path = VagrantPlugins::HostLinux::Cap::NFS::NFS_EXPORTS_PATH
|
|
VagrantPlugins::HostLinux::Cap::NFS.send(:remove_const, :NFS_EXPORTS_PATH)
|
|
VagrantPlugins::HostLinux::Cap::NFS.const_set(:NFS_EXPORTS_PATH, tmp_exports_path.to_s)
|
|
allow(Vagrant::Util::Subprocess).to receive(:execute).with("systemctl", "list-units", any_args).
|
|
and_return(Vagrant::Util::Subprocess::Result.new(1, "", ""))
|
|
allow(Vagrant::Util::Platform).to receive(:systemd?).and_return(false)
|
|
end
|
|
|
|
after do
|
|
VagrantPlugins::HostLinux::Cap::NFS.send(:remove_const, :NFS_EXPORTS_PATH)
|
|
VagrantPlugins::HostLinux::Cap::NFS.const_set(:NFS_EXPORTS_PATH, @original_exports_path)
|
|
VagrantPlugins::HostLinux::Cap::NFS.reset!
|
|
File.unlink(tmp_exports_path.to_s) if File.exist?(tmp_exports_path.to_s)
|
|
@tmp_exports = nil
|
|
end
|
|
|
|
describe ".nfs_service_name_systemd" do
|
|
let(:cap){ VagrantPlugins::HostLinux::Cap::NFS }
|
|
|
|
context "without service match" do
|
|
it "should use default service name" do
|
|
expect(cap.nfs_service_name_systemd).to eq(cap.const_get(:NFS_DEFAULT_NAME_SYSTEMD))
|
|
end
|
|
end
|
|
|
|
context "with service match" do
|
|
let(:custom_nfs_service_name){ "custom-nfs-server-service-name" }
|
|
before{ expect(Vagrant::Util::Subprocess).to receive(:execute).with("systemctl", "list-units", any_args).
|
|
and_return(Vagrant::Util::Subprocess::Result.new(0, custom_nfs_service_name, "")) }
|
|
|
|
it "should use the matched service name" do
|
|
expect(cap.nfs_service_name_systemd).to eq(custom_nfs_service_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe ".nfs_service_name_sysv" do
|
|
let(:cap){ VagrantPlugins::HostLinux::Cap::NFS }
|
|
|
|
context "without service match" do
|
|
it "should use default service name" do
|
|
expect(cap.nfs_service_name_sysv).to eq(cap.const_get(:NFS_DEFAULT_NAME_SYSV))
|
|
end
|
|
end
|
|
|
|
context "with service match" do
|
|
let(:custom_nfs_service_name){ "/etc/init.d/custom-nfs-server-service-name" }
|
|
before{ expect(Dir).to receive(:glob).with(/.+init\.d.+/).and_return([custom_nfs_service_name]) }
|
|
|
|
it "should use the matched service name" do
|
|
expect(cap.nfs_service_name_sysv).to eq(File.basename(custom_nfs_service_name))
|
|
end
|
|
end
|
|
end
|
|
|
|
describe ".nfs_check_command" do
|
|
let(:cap){ caps.get(:nfs_check_command) }
|
|
|
|
context "without systemd" do
|
|
before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(false) }
|
|
|
|
it "should use init.d script" do
|
|
expect(cap.nfs_check_command(env)).to include("init.d")
|
|
end
|
|
end
|
|
context "with systemd" do
|
|
before do
|
|
expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(true)
|
|
end
|
|
|
|
it "should use systemctl" do
|
|
expect(cap.nfs_check_command(env)).to include("systemctl")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe ".nfs_start_command" do
|
|
let(:cap){ caps.get(:nfs_start_command) }
|
|
|
|
context "without systemd" do
|
|
before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(false) }
|
|
|
|
it "should use init.d script" do
|
|
expect(cap.nfs_start_command(env)).to include("init.d")
|
|
end
|
|
end
|
|
context "with systemd" do
|
|
before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(true) }
|
|
|
|
it "should use systemctl" do
|
|
expect(cap.nfs_start_command(env)).to include("systemctl")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe ".nfs_export" do
|
|
|
|
let(:cap){ caps.get(:nfs_export) }
|
|
|
|
before do
|
|
allow(env).to receive(:host).and_return(host)
|
|
allow(host).to receive(:capability).with(:nfs_apply_command).and_return("/bin/true")
|
|
allow(host).to receive(:capability).with(:nfs_check_command).and_return("/bin/true")
|
|
allow(host).to receive(:capability).with(:nfs_start_command).and_return("/bin/true")
|
|
allow(ui).to receive(:info)
|
|
allow(Vagrant::Util::Subprocess).to receive(:execute).and_call_original
|
|
allow(Vagrant::Util::Subprocess).to receive(:execute).with("sudo", "/bin/true").and_return(double(:result, exit_code: 0))
|
|
allow(Vagrant::Util::Subprocess).to receive(:execute).with("/bin/true").and_return(double(:result, exit_code: 0))
|
|
end
|
|
|
|
it "should export new entries" do
|
|
cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], "tmp" => {:hostpath => "/tmp"})
|
|
exports_content = File.read(exports_path)
|
|
expect(exports_content).to match(/\/tmp.*127\.0\.0\.1/)
|
|
end
|
|
|
|
it "should not remove existing entries" do
|
|
File.write(exports_path, "/custom/directory hostname1(rw,sync,no_subtree_check)")
|
|
cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], "tmp" => {:hostpath => "/tmp"})
|
|
exports_content = File.read(exports_path)
|
|
expect(exports_content).to match(/\/tmp.*127\.0\.0\.1/)
|
|
expect(exports_content).to match(/\/custom\/directory.*hostname1/)
|
|
end
|
|
|
|
it "should remove entries no longer valid" do
|
|
valid_id = SecureRandom.uuid
|
|
other_id = SecureRandom.uuid
|
|
content =<<-EOH
|
|
# VAGRANT-BEGIN: #{Process.uid} #{other_id}
|
|
"/tmp" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
|
|
# VAGRANT-END: #{Process.uid} #{other_id}
|
|
# VAGRANT-BEGIN: #{Process.uid} #{valid_id}
|
|
"/var" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
|
|
# VAGRANT-END: #{Process.uid} #{valid_id}
|
|
EOH
|
|
File.write(exports_path, content)
|
|
cap.nfs_export(env, ui, valid_id, ["127.0.0.1"], "home" => {:hostpath => "/home"})
|
|
exports_content = File.read(exports_path)
|
|
expect(exports_content).to include("/home")
|
|
expect(exports_content).to include("/tmp")
|
|
expect(exports_content).not_to include("/var")
|
|
end
|
|
|
|
it "throws an exception with at least 2 different nfs options" do
|
|
folders = {"/vagrant"=>
|
|
{:hostpath=>"/home/vagrant",
|
|
:linux__nfs_options=>["rw","all_squash"]},
|
|
"/var/www/project"=>
|
|
{:hostpath=>"/home/vagrant",
|
|
:linux__nfs_options=>["rw","sync"]}}
|
|
|
|
expect { cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], folders) }.
|
|
to raise_error Vagrant::Errors::NFSDupePerms
|
|
end
|
|
|
|
it "writes only 1 hostpath for multiple exports" do
|
|
folders = {"/vagrant"=>
|
|
{:hostpath=>"/home/vagrant",
|
|
:linux__nfs_options=>["rw","all_squash"]},
|
|
"/var/www/otherproject"=>
|
|
{:hostpath=>"/newhome/otherproject",
|
|
:linux__nfs_options=>["rw","all_squash"]},
|
|
"/var/www/project"=>
|
|
{:hostpath=>"/home/vagrant",
|
|
:linux__nfs_options=>["rw","all_squash"]}}
|
|
valid_id = SecureRandom.uuid
|
|
content =<<-EOH
|
|
\n# VAGRANT-BEGIN: #{Process.uid} #{valid_id}
|
|
"/home/vagrant" 127.0.0.1(rw,all_squash,anonuid=,anongid=,fsid=)
|
|
"/newhome/otherproject" 127.0.0.1(rw,all_squash,anonuid=,anongid=,fsid=)
|
|
# VAGRANT-END: #{Process.uid} #{valid_id}
|
|
EOH
|
|
|
|
cap.nfs_export(env, ui, valid_id, ["127.0.0.1"], folders)
|
|
exports_content = File.read(exports_path)
|
|
expect(exports_content).to eq(content)
|
|
end
|
|
|
|
end
|
|
|
|
describe ".nfs_prune" do
|
|
|
|
let(:cap){ caps.get(:nfs_prune) }
|
|
|
|
before do
|
|
allow(ui).to receive(:info)
|
|
allow(Vagrant::Util::Subprocess).to receive(:execute).with("mv", any_args).
|
|
and_call_original
|
|
end
|
|
|
|
it "should remove entries no longer valid" do
|
|
invalid_id = SecureRandom.uuid
|
|
valid_id = SecureRandom.uuid
|
|
content =<<-EOH
|
|
# VAGRANT-BEGIN: #{Process.uid} #{invalid_id}
|
|
"/tmp" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
|
|
# VAGRANT-END: #{Process.uid} #{invalid_id}
|
|
# VAGRANT-BEGIN: #{Process.uid} #{valid_id}
|
|
"/var" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=)
|
|
# VAGRANT-END: #{Process.uid} #{valid_id}
|
|
EOH
|
|
File.write(exports_path, content)
|
|
cap.nfs_prune(env, ui, [valid_id])
|
|
exports_content = File.read(exports_path)
|
|
expect(exports_content).to include(valid_id)
|
|
expect(exports_content).not_to include(invalid_id)
|
|
expect(exports_content).to include("/var")
|
|
expect(exports_content).not_to include("/tmp")
|
|
end
|
|
end
|
|
|
|
describe ".nfs_write_exports" do
|
|
|
|
before do
|
|
File.write(tmp_exports_path, "original content")
|
|
allow(Vagrant::Util::Subprocess).to receive(:execute).with("mv", any_args).
|
|
and_call_original
|
|
end
|
|
|
|
it "should write updated contents to file" do
|
|
described_class.nfs_write_exports("new content")
|
|
exports_content = File.read(exports_path)
|
|
expect(exports_content).to include("new content")
|
|
expect(exports_content).not_to include("original content")
|
|
end
|
|
|
|
it "should only update contents if different" do
|
|
original_stat = File.stat(exports_path)
|
|
described_class.nfs_write_exports("original content")
|
|
updated_stat = File.stat(exports_path)
|
|
expect(original_stat).to eq(updated_stat)
|
|
end
|
|
|
|
it "should retain existing file permissions" do
|
|
File.chmod(0600, exports_path)
|
|
original_stat = File.stat(exports_path)
|
|
described_class.nfs_write_exports("original content")
|
|
updated_stat = File.stat(exports_path)
|
|
expect(original_stat.mode).to eq(updated_stat.mode)
|
|
end
|
|
|
|
it "should raise exception when failing to move new exports file" do
|
|
expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(
|
|
Vagrant::Util::Subprocess::Result.new(1, "Failed to move file", "")
|
|
)
|
|
expect{ described_class.nfs_write_exports("new content") }.to raise_error(Vagrant::Errors::NFSExportsFailed)
|
|
end
|
|
|
|
it "should retain existing file owner and group IDs" do
|
|
pending("investigate using a simulated FS to test")
|
|
test_with_simulated_fs
|
|
end
|
|
|
|
it "should raise custom exception when chown fails" do
|
|
pending("investigate using a simulated FS to test")
|
|
test_with_simulated_fs
|
|
end
|
|
end
|
|
end
|