2014-02-03 15:40:02 +00:00
|
|
|
require File.expand_path("../../../../base", __FILE__)
|
|
|
|
|
|
|
|
require Vagrant.source_root.join("plugins/kernel_v2/config/vm")
|
|
|
|
|
|
|
|
describe VagrantPlugins::Kernel_V2::VMConfig do
|
2014-03-18 18:36:40 +00:00
|
|
|
include_context "unit"
|
|
|
|
|
2014-02-03 15:40:02 +00:00
|
|
|
subject { described_class.new }
|
|
|
|
|
2014-01-24 17:16:37 +00:00
|
|
|
let(:machine) { double("machine") }
|
|
|
|
|
2014-03-13 15:31:59 +00:00
|
|
|
def assert_invalid
|
|
|
|
errors = subject.validate(machine)
|
|
|
|
if !errors.values.any? { |v| !v.empty? }
|
|
|
|
raise "No errors: #{errors.inspect}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-24 17:16:37 +00:00
|
|
|
def assert_valid
|
|
|
|
errors = subject.validate(machine)
|
|
|
|
if !errors.values.all? { |v| v.empty? }
|
|
|
|
raise "Errors: #{errors.inspect}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
2014-02-06 05:24:34 +00:00
|
|
|
env = double("env")
|
|
|
|
env.stub(root_path: nil)
|
|
|
|
machine.stub(env: env)
|
2014-01-24 17:16:37 +00:00
|
|
|
machine.stub(provider_config: nil)
|
2014-04-10 03:13:38 +00:00
|
|
|
machine.stub(provider_options: {})
|
2014-01-24 17:16:37 +00:00
|
|
|
|
|
|
|
subject.box = "foo"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is valid with test defaults" do
|
|
|
|
subject.finalize!
|
|
|
|
assert_valid
|
|
|
|
end
|
|
|
|
|
2014-02-05 23:35:37 +00:00
|
|
|
describe "#base_mac" do
|
|
|
|
it "defaults properly" do
|
|
|
|
subject.finalize!
|
|
|
|
expect(subject.base_mac).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-10 03:13:38 +00:00
|
|
|
describe "#box" do
|
|
|
|
it "is required" do
|
|
|
|
subject.box = nil
|
|
|
|
subject.finalize!
|
|
|
|
assert_invalid
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is not required if the provider says so" do
|
|
|
|
machine.provider_options[:box_optional] = true
|
|
|
|
subject.box = nil
|
|
|
|
subject.finalize!
|
|
|
|
assert_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-24 23:27:01 +00:00
|
|
|
context "#box_check_update" do
|
2014-02-03 10:40:15 +00:00
|
|
|
it "defaults to true" do
|
2014-01-24 23:27:01 +00:00
|
|
|
subject.finalize!
|
|
|
|
|
2014-02-03 10:40:15 +00:00
|
|
|
expect(subject.box_check_update).to be_true
|
2014-01-24 23:27:01 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-05 23:35:37 +00:00
|
|
|
describe "#box_url" do
|
2014-01-24 20:58:01 +00:00
|
|
|
it "defaults to nil" do
|
2014-02-05 23:35:37 +00:00
|
|
|
subject.finalize!
|
2014-01-24 20:58:01 +00:00
|
|
|
|
2014-02-05 23:35:37 +00:00
|
|
|
expect(subject.box_url).to be_nil
|
|
|
|
end
|
2014-01-24 20:58:01 +00:00
|
|
|
|
|
|
|
it "turns into an array" do
|
|
|
|
subject.box_url = "foo"
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
expect(subject.box_url).to eq(
|
|
|
|
["foo"])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "keeps in array" do
|
|
|
|
subject.box_url = ["foo", "bar"]
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
expect(subject.box_url).to eq(
|
|
|
|
["foo", "bar"])
|
|
|
|
end
|
2014-02-05 23:35:37 +00:00
|
|
|
end
|
|
|
|
|
2014-01-24 17:16:37 +00:00
|
|
|
context "#box_version" do
|
2014-04-03 02:45:49 +00:00
|
|
|
it "defaults to nil" do
|
2014-01-24 17:16:37 +00:00
|
|
|
subject.finalize!
|
|
|
|
|
2014-04-03 02:45:49 +00:00
|
|
|
expect(subject.box_version).to be_nil
|
2014-01-24 17:16:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "errors if invalid version" do
|
|
|
|
subject.box_version = "nope"
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
expect { assert_valid }.to raise_error(RuntimeError)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can have complex constraints" do
|
|
|
|
subject.box_version = ">= 0, ~> 1.0"
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
assert_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-03-11 05:59:25 +00:00
|
|
|
describe "#communicator" do
|
|
|
|
it "is nil by default" do
|
|
|
|
subject.finalize!
|
|
|
|
expect(subject.communicator).to be_nil
|
|
|
|
end
|
2014-08-11 04:05:29 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "#define" do
|
|
|
|
it "should allow regular names" do
|
|
|
|
subject.define "foo"
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
assert_valid
|
|
|
|
end
|
|
|
|
|
|
|
|
[
|
|
|
|
"foo [1]",
|
|
|
|
"bar {2}",
|
2014-08-11 16:21:31 +00:00
|
|
|
"foo/bar",
|
2014-08-11 04:05:29 +00:00
|
|
|
].each do |name|
|
|
|
|
it "should disallow names with brackets" do
|
|
|
|
subject.define name
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
assert_invalid
|
|
|
|
end
|
|
|
|
end
|
2014-03-11 05:59:25 +00:00
|
|
|
end
|
|
|
|
|
2014-03-06 02:50:31 +00:00
|
|
|
describe "#guest" do
|
|
|
|
it "is nil by default" do
|
|
|
|
subject.finalize!
|
|
|
|
expect(subject.guest).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is symbolized" do
|
|
|
|
subject.guest = "foo"
|
|
|
|
subject.finalize!
|
|
|
|
expect(subject.guest).to eq(:foo)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-05-08 16:10:13 +00:00
|
|
|
describe "#hostname" do
|
|
|
|
["a", "foo", "foo-bar", "baz0"].each do |valid|
|
|
|
|
it "is valid: #{valid}" do
|
|
|
|
subject.hostname = valid
|
|
|
|
subject.finalize!
|
|
|
|
assert_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-05 23:35:37 +00:00
|
|
|
describe "#network(s)" do
|
2014-05-09 00:00:55 +00:00
|
|
|
it "defaults to forwarding SSH by default" do
|
2014-02-05 23:35:37 +00:00
|
|
|
subject.finalize!
|
|
|
|
n = subject.networks
|
|
|
|
expect(n.length).to eq(1)
|
|
|
|
expect(n[0][0]).to eq(:forwarded_port)
|
|
|
|
expect(n[0][1][:guest]).to eq(22)
|
|
|
|
expect(n[0][1][:host]).to eq(2222)
|
|
|
|
expect(n[0][1][:host_ip]).to eq("127.0.0.1")
|
|
|
|
expect(n[0][1][:id]).to eq("ssh")
|
|
|
|
end
|
|
|
|
|
2014-05-09 00:00:55 +00:00
|
|
|
it "defaults to forwarding WinRM if communicator is winrm" do
|
|
|
|
subject.communicator = "winrm"
|
|
|
|
subject.finalize!
|
|
|
|
n = subject.networks
|
2014-09-04 21:19:47 +00:00
|
|
|
expect(n.length).to eq(2)
|
2014-05-09 00:00:55 +00:00
|
|
|
expect(n[0][0]).to eq(:forwarded_port)
|
|
|
|
expect(n[0][1][:guest]).to eq(5985)
|
|
|
|
expect(n[0][1][:host]).to eq(55985)
|
|
|
|
expect(n[0][1][:host_ip]).to eq("127.0.0.1")
|
|
|
|
expect(n[0][1][:id]).to eq("winrm")
|
2014-09-04 21:19:47 +00:00
|
|
|
|
|
|
|
expect(n[1][0]).to eq(:forwarded_port)
|
|
|
|
expect(n[1][1][:guest]).to eq(22)
|
|
|
|
expect(n[1][1][:host]).to eq(2222)
|
|
|
|
expect(n[1][1][:id]).to eq("ssh")
|
2014-05-09 00:00:55 +00:00
|
|
|
end
|
|
|
|
|
2014-04-15 17:37:08 +00:00
|
|
|
it "allows overriding SSH" do
|
|
|
|
subject.network "forwarded_port",
|
|
|
|
guest: 22, host: 14100, id: "ssh"
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
n = subject.networks
|
|
|
|
expect(n.length).to eq(1)
|
|
|
|
expect(n[0][0]).to eq(:forwarded_port)
|
|
|
|
expect(n[0][1][:guest]).to eq(22)
|
|
|
|
expect(n[0][1][:host]).to eq(14100)
|
|
|
|
expect(n[0][1][:id]).to eq("ssh")
|
|
|
|
end
|
|
|
|
|
2014-05-09 00:00:55 +00:00
|
|
|
it "allows overriding WinRM" do
|
|
|
|
subject.communicator = :winrm
|
|
|
|
subject.network "forwarded_port",
|
|
|
|
guest: 22, host: 14100, id: "winrm"
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
n = subject.networks
|
2014-09-04 21:19:47 +00:00
|
|
|
expect(n.length).to eq(2)
|
2014-05-09 00:00:55 +00:00
|
|
|
expect(n[0][0]).to eq(:forwarded_port)
|
|
|
|
expect(n[0][1][:guest]).to eq(22)
|
|
|
|
expect(n[0][1][:host]).to eq(14100)
|
|
|
|
expect(n[0][1][:id]).to eq("winrm")
|
|
|
|
end
|
|
|
|
|
2014-02-05 23:35:37 +00:00
|
|
|
it "turns all forwarded port ports to ints" do
|
|
|
|
subject.network "forwarded_port",
|
|
|
|
guest: "45", host: "4545", id: "test"
|
|
|
|
subject.finalize!
|
|
|
|
n = subject.networks.find do |type, data|
|
|
|
|
type == :forwarded_port && data[:id] == "test"
|
|
|
|
end
|
|
|
|
expect(n).to_not be_nil
|
|
|
|
expect(n[1][:guest]).to eq(45)
|
|
|
|
expect(n[1][:host]).to eq(4545)
|
|
|
|
end
|
2014-03-13 15:31:59 +00:00
|
|
|
|
|
|
|
it "is an error if forwarding a port too low" do
|
|
|
|
subject.network "forwarded_port",
|
|
|
|
guest: "45", host: "-5"
|
|
|
|
subject.finalize!
|
|
|
|
assert_invalid
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is an error if forwarding a port too high" do
|
|
|
|
subject.network "forwarded_port",
|
|
|
|
guest: "45", host: "74545"
|
|
|
|
subject.finalize!
|
|
|
|
assert_invalid
|
|
|
|
end
|
2014-02-05 23:35:37 +00:00
|
|
|
end
|
|
|
|
|
2014-04-09 21:57:16 +00:00
|
|
|
describe "#post_up_message" do
|
|
|
|
it "defaults to empty string" do
|
|
|
|
subject.finalize!
|
|
|
|
expect(subject.post_up_message).to eq("")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can be set" do
|
|
|
|
subject.post_up_message = "foo"
|
|
|
|
subject.finalize!
|
|
|
|
expect(subject.post_up_message).to eq("foo")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-10-23 22:32:54 +00:00
|
|
|
describe "#provider and #__providers" do
|
|
|
|
it "returns the providers in order" do
|
|
|
|
subject.provider "foo"
|
|
|
|
subject.provider "bar"
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
expect(subject.__providers).to eq([:foo, :bar])
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "merging" do
|
|
|
|
it "prioritizes new orders in later configs" do
|
|
|
|
subject.provider "foo"
|
|
|
|
|
|
|
|
other = described_class.new
|
|
|
|
other.provider "bar"
|
|
|
|
|
|
|
|
merged = subject.merge(other)
|
|
|
|
|
|
|
|
expect(merged.__providers).to eq([:foo, :bar])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "prioritizes duplicates in new orders in later configs" do
|
|
|
|
subject.provider "foo"
|
|
|
|
|
|
|
|
other = described_class.new
|
|
|
|
other.provider "bar"
|
|
|
|
other.provider "foo"
|
|
|
|
|
|
|
|
merged = subject.merge(other)
|
|
|
|
|
|
|
|
expect(merged.__providers).to eq([:foo, :bar])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-11 19:54:52 +00:00
|
|
|
describe "#provider and #get_provider_config" do
|
|
|
|
it "compiles the configurations for a provider" do
|
|
|
|
subject.provider "virtualbox" do |vb|
|
|
|
|
vb.gui = true
|
|
|
|
end
|
|
|
|
|
|
|
|
subject.provider "virtualbox" do |vb|
|
|
|
|
vb.name = "foo"
|
|
|
|
end
|
|
|
|
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
config = subject.get_provider_config(:virtualbox)
|
|
|
|
expect(config.name).to eq("foo")
|
|
|
|
expect(config.gui).to be_true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an exception if there is a problem loading" do
|
|
|
|
subject.provider "virtualbox" do |vb|
|
|
|
|
# Purposeful bad variable
|
|
|
|
vm.foo = "bar"
|
|
|
|
end
|
|
|
|
|
|
|
|
expect { subject.finalize! }.
|
|
|
|
to raise_error(Vagrant::Errors::VagrantfileLoadError)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-03 15:40:02 +00:00
|
|
|
describe "#provision" do
|
|
|
|
it "stores the provisioners" do
|
|
|
|
subject.provision("shell", inline: "foo")
|
2014-04-09 22:28:44 +00:00
|
|
|
subject.provision("shell", inline: "bar", run: "always") { |s| s.path = "baz" }
|
2014-02-03 15:40:02 +00:00
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
r = subject.provisioners
|
|
|
|
expect(r.length).to eql(2)
|
2014-04-09 22:28:44 +00:00
|
|
|
expect(r[0].run).to be_nil
|
2014-02-03 15:40:02 +00:00
|
|
|
expect(r[0].config.inline).to eql("foo")
|
|
|
|
expect(r[1].config.inline).to eql("bar")
|
2014-02-04 03:41:11 +00:00
|
|
|
expect(r[1].config.path).to eql("baz")
|
2014-04-09 22:28:44 +00:00
|
|
|
expect(r[1].run).to eql(:always)
|
2014-02-03 15:40:02 +00:00
|
|
|
end
|
2014-02-03 15:42:47 +00:00
|
|
|
|
2014-02-03 15:56:39 +00:00
|
|
|
it "allows provisioner settings to be overriden" do
|
2014-02-04 03:41:11 +00:00
|
|
|
subject.provision("shell", path: "foo", id: "s") { |s| s.inline = "foo" }
|
|
|
|
subject.provision("shell", inline: "bar", id: "s") { |s| s.args = "bar" }
|
2014-02-03 15:56:39 +00:00
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
r = subject.provisioners
|
|
|
|
expect(r.length).to eql(1)
|
2014-02-04 03:41:11 +00:00
|
|
|
expect(r[0].config.args).to eql("bar")
|
2014-02-03 15:56:39 +00:00
|
|
|
expect(r[0].config.inline).to eql("bar")
|
|
|
|
expect(r[0].config.path).to eql("foo")
|
|
|
|
end
|
|
|
|
|
2014-02-03 15:42:47 +00:00
|
|
|
it "marks as invalid if a bad name" do
|
|
|
|
subject.provision("nope", inline: "foo")
|
|
|
|
subject.finalize!
|
|
|
|
|
|
|
|
r = subject.provisioners
|
|
|
|
expect(r.length).to eql(1)
|
|
|
|
expect(r[0]).to be_invalid
|
|
|
|
end
|
2014-02-03 20:30:01 +00:00
|
|
|
|
2014-03-18 18:36:40 +00:00
|
|
|
it "allows provisioners that don't define any config" do
|
|
|
|
register_plugin("2") do |p|
|
|
|
|
p.name "foo"
|
|
|
|
# This plugin registers a dummy provisioner
|
|
|
|
# without registering a provisioner config
|
|
|
|
p.provisioner(:foo) do
|
|
|
|
Class.new Vagrant::plugin("2", :provisioner)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
subject.provision("foo") do |c|
|
|
|
|
c.bar = "baz"
|
|
|
|
end
|
|
|
|
|
|
|
|
# This should succeed without errors
|
|
|
|
expect{ subject.finalize! }.to_not raise_error
|
|
|
|
end
|
|
|
|
|
2014-02-03 20:30:01 +00:00
|
|
|
describe "merging" do
|
2014-04-09 22:28:44 +00:00
|
|
|
it "ignores non-overriding runs" do
|
|
|
|
subject.provision("shell", inline: "foo", run: "once")
|
|
|
|
|
|
|
|
other = described_class.new
|
|
|
|
other.provision("shell", inline: "bar", run: "always")
|
|
|
|
|
|
|
|
merged = subject.merge(other)
|
|
|
|
merged_provs = merged.provisioners
|
|
|
|
|
|
|
|
expect(merged_provs.length).to eql(2)
|
|
|
|
expect(merged_provs[0].run).to eq("once")
|
|
|
|
expect(merged_provs[1].run).to eq("always")
|
|
|
|
end
|
|
|
|
|
2014-02-03 20:30:01 +00:00
|
|
|
it "copies the configs" do
|
|
|
|
subject.provision("shell", inline: "foo")
|
|
|
|
subject_provs = subject.provisioners
|
|
|
|
|
|
|
|
other = described_class.new
|
|
|
|
other.provision("shell", inline: "bar")
|
|
|
|
|
|
|
|
merged = subject.merge(other)
|
|
|
|
merged_provs = merged.provisioners
|
|
|
|
|
|
|
|
expect(merged_provs.length).to eql(2)
|
|
|
|
expect(merged_provs[0].config.inline).
|
|
|
|
to eq(subject_provs[0].config.inline)
|
|
|
|
expect(merged_provs[0].config.object_id).
|
|
|
|
to_not eq(subject_provs[0].config.object_id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "uses the proper order when merging overrides" do
|
|
|
|
subject.provision("shell", inline: "foo", id: "original")
|
|
|
|
subject.provision("shell", inline: "other", id: "other")
|
|
|
|
|
|
|
|
other = described_class.new
|
|
|
|
other.provision("shell", inline: "bar")
|
|
|
|
other.provision("shell", inline: "foo-overload", id: "original")
|
|
|
|
|
|
|
|
merged = subject.merge(other)
|
|
|
|
merged_provs = merged.provisioners
|
|
|
|
|
|
|
|
expect(merged_provs.length).to eql(3)
|
|
|
|
expect(merged_provs[0].config.inline).
|
|
|
|
to eq("other")
|
|
|
|
expect(merged_provs[1].config.inline).
|
|
|
|
to eq("bar")
|
|
|
|
expect(merged_provs[2].config.inline).
|
|
|
|
to eq("foo-overload")
|
|
|
|
end
|
2014-02-03 21:03:20 +00:00
|
|
|
|
|
|
|
it "can preserve order for overrides" do
|
|
|
|
subject.provision("shell", inline: "foo", id: "original")
|
|
|
|
subject.provision("shell", inline: "other", id: "other")
|
|
|
|
|
|
|
|
other = described_class.new
|
|
|
|
other.provision("shell", inline: "bar")
|
|
|
|
other.provision(
|
|
|
|
"shell", inline: "foo-overload", id: "original",
|
|
|
|
preserve_order: true)
|
|
|
|
|
|
|
|
merged = subject.merge(other)
|
|
|
|
merged_provs = merged.provisioners
|
|
|
|
|
|
|
|
expect(merged_provs.length).to eql(3)
|
|
|
|
expect(merged_provs[0].config.inline).
|
|
|
|
to eq("foo-overload")
|
|
|
|
expect(merged_provs[1].config.inline).
|
|
|
|
to eq("other")
|
|
|
|
expect(merged_provs[2].config.inline).
|
|
|
|
to eq("bar")
|
|
|
|
end
|
2014-02-03 20:30:01 +00:00
|
|
|
end
|
2014-02-03 15:40:02 +00:00
|
|
|
end
|
2014-02-05 23:35:37 +00:00
|
|
|
|
|
|
|
describe "#synced_folder(s)" do
|
|
|
|
it "defaults to sharing the current directory" do
|
|
|
|
subject.finalize!
|
|
|
|
sf = subject.synced_folders
|
|
|
|
expect(sf.length).to eq(1)
|
|
|
|
expect(sf).to have_key("/vagrant")
|
|
|
|
expect(sf["/vagrant"][:disabled]).to_not be
|
|
|
|
end
|
|
|
|
|
|
|
|
it "allows overriding settings on the /vagrant sf" do
|
|
|
|
subject.synced_folder(".", "/vagrant", disabled: true)
|
|
|
|
subject.finalize!
|
|
|
|
sf = subject.synced_folders
|
|
|
|
expect(sf.length).to eq(1)
|
|
|
|
expect(sf).to have_key("/vagrant")
|
|
|
|
expect(sf["/vagrant"][:disabled]).to be_true
|
|
|
|
end
|
2014-05-09 01:39:13 +00:00
|
|
|
|
|
|
|
it "allows overriding previously set options" do
|
|
|
|
subject.synced_folder(".", "/vagrant", disabled: true)
|
|
|
|
subject.synced_folder(".", "/vagrant", foo: :bar)
|
|
|
|
subject.finalize!
|
|
|
|
sf = subject.synced_folders
|
|
|
|
expect(sf.length).to eq(1)
|
|
|
|
expect(sf).to have_key("/vagrant")
|
2014-05-13 22:23:38 +00:00
|
|
|
expect(sf["/vagrant"][:disabled]).to be_false
|
2014-05-09 01:39:13 +00:00
|
|
|
expect(sf["/vagrant"][:foo]).to eq(:bar)
|
|
|
|
end
|
2014-02-05 23:35:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "#usable_port_range" do
|
|
|
|
it "defaults properly" do
|
|
|
|
subject.finalize!
|
|
|
|
expect(subject.usable_port_range).to eq(
|
|
|
|
Range.new(2200, 2250))
|
|
|
|
end
|
|
|
|
end
|
2014-02-03 15:40:02 +00:00
|
|
|
end
|