Check file size prior to upload and automatically adjust options

When uploading box file, check if the size is greater than
    5GB. If the size is larger and the direct to storage option
    is enabled, disable the option due to current 5GB restriction
    on direct uploads.
This commit is contained in:
Chris Roberts 2021-03-23 15:11:32 -07:00
parent f603392804
commit fd4da92245
7 changed files with 103 additions and 6 deletions

View File

@ -11,6 +11,7 @@ module Vagrant
autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access' autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access'
autoload :GuestInspection, 'vagrant/util/guest_inspection' autoload :GuestInspection, 'vagrant/util/guest_inspection'
autoload :LoggingFormatter, 'vagrant/util/logging_formatter' autoload :LoggingFormatter, 'vagrant/util/logging_formatter'
autoload :Numeric, 'vagrant/util/numeric'
autoload :Platform, 'vagrant/util/platform' autoload :Platform, 'vagrant/util/platform'
autoload :Retryable, 'vagrant/util/retryable' autoload :Retryable, 'vagrant/util/retryable'
autoload :SafeExec, 'vagrant/util/safe_exec' autoload :SafeExec, 'vagrant/util/safe_exec'

View File

@ -49,6 +49,26 @@ module Vagrant
bytes bytes
end end
# Convert bytes to a user friendly string representation
#
# @param [Numeric] bytes Number of bytes to represent
# @return [String] user friendly output
def bytes_to_string(bytes)
# We want to locate the size that will return the
# smallest whole value number
BYTES_CONVERSION_MAP.sort { |a, b|
b.last <=> a.last
}.each do |suffix, size|
val = bytes.to_f / size
next if val < 1
val = sprintf("%.2f", val)
val.slice!(-1, 1) while val.end_with?("0")
val.slice!(-1, 1) if val.end_with?(".")
return "#{val}#{suffix}"
end
"#{bytes} byte#{"s" if bytes > 1}"
end
# Rounds actual value to two decimal places # Rounds actual value to two decimal places
# #
# @param [Integer] bytes # @param [Integer] bytes

View File

@ -75,6 +75,10 @@ en:
Updated provider %{provider} on %{org}/%{box_name} for version %{version} Updated provider %{provider} on %{org}/%{box_name} for version %{version}
not_found: |- not_found: |-
Failed to locate %{provider_name} provider for %{org}/%{box_name} on version %{version} Failed to locate %{provider_name} provider for %{org}/%{box_name} on version %{version}
direct_disable: |-
Vagrant is automatically disabling direct upload to backend storage.
Uploads directly to backend storage are currently only supported for
files 5G in size or smaller. Box file to upload is: %{size}
version: version:
create_success: |- create_success: |-
Created version %{version} on %{org}/%{box_name} for version %{version} Created version %{version} on %{org}/%{box_name} for version %{version}

View File

@ -58,6 +58,16 @@ module VagrantPlugins
access_token: access_token access_token: access_token
) )
# Include size check on file and disable direct if over 5G
if options[:direct]
fsize = File.stat(file).size
if fsize > (5 * Vagrant::Util::Numeric::GIGABYTE)
box_size = Vagrant::Util::Numeric.bytes_to_string(fsize)
@env.ui.warn(I18n.t("cloud_command.provider.direct_disable", size: box_size))
options[:direct] = false
end
end
with_provider(account: account, org: org, box: box, version: version, provider: provider) do |p| with_provider(account: account, org: org, box: box, version: version, provider: provider) do |p|
p.upload(direct: options[:direct]) do |upload_url| p.upload(direct: options[:direct]) do |upload_url|
m = options[:direct] ? :put : :put m = options[:direct] ? :put : :put

View File

@ -129,6 +129,16 @@ module VagrantPlugins
def upload_box_file(provider, box_file, options={}) def upload_box_file(provider, box_file, options={})
box_file = File.absolute_path(box_file) box_file = File.absolute_path(box_file)
@env.ui.info(I18n.t("cloud_command.publish.upload_provider", file: box_file)) @env.ui.info(I18n.t("cloud_command.publish.upload_provider", file: box_file))
# Include size check on file and disable direct if over 5G
if options[:direct_upload]
fsize = File.stat(box_file).size
if fsize > (5 * Vagrant::Util::Numeric::GIGABYTE)
box_size = Vagrant::Util::Numeric.bytes_to_string(fsize)
@env.ui.warn(I18n.t("cloud_command.provider.direct_disable", size: box_size))
options[:direct_upload] = false
end
end
provider.upload(direct: options[:direct_upload]) do |upload_url| provider.upload(direct: options[:direct_upload]) do |upload_url|
Vagrant::Util::Uploader.new(upload_url, box_file, ui: @env.ui, method: :put).upload! Vagrant::Util::Uploader.new(upload_url, box_file, ui: @env.ui, method: :put).upload!
end end

View File

@ -15,6 +15,7 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Upload do
let(:version) { double("version", version: box_version, provdiers: [provider]) } let(:version) { double("version", version: box_version, provdiers: [provider]) }
let(:provider) { double("provider", name: box_version_provider) } let(:provider) { double("provider", name: box_version_provider) }
let(:provider_file) { double("provider-file") } let(:provider_file) { double("provider-file") }
let(:provider_file_size) { 1 }
describe "#upload_provider" do describe "#upload_provider" do
let(:argv) { [] } let(:argv) { [] }
@ -42,6 +43,8 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Upload do
allow(uploader).to receive(:upload!) allow(uploader).to receive(:upload!)
allow(Vagrant::UI::Prefixed).to receive(:new).with(ui, "cloud").and_return(ui) allow(Vagrant::UI::Prefixed).to receive(:new).with(ui, "cloud").and_return(ui)
allow(Vagrant::Util::Uploader).to receive(:new).and_return(uploader) allow(Vagrant::Util::Uploader).to receive(:new).and_return(uploader)
allow(File).to receive(:stat).with(provider_file).
and_return(double("provider-stat", size: provider_file_size))
end end
subject { described_class.new(argv, env) } subject { described_class.new(argv, env) }
@ -91,6 +94,28 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Upload do
end end
subject.upload_provider(org_name, box_name, box_version, box_version_provider, provider_file, access_token, options) subject.upload_provider(org_name, box_name, box_version, box_version_provider, provider_file, access_token, options)
end end
context "when file size is 5GB" do
let(:provider_file_size) { 5368709120 }
it "should use direct upload" do
expect(provider).to receive(:upload) do |**args|
expect(args[:direct]).to be_truthy
end
subject.upload_provider(org_name, box_name, box_version, box_version_provider, provider_file, access_token, options)
end
end
context "when file size is greater than 5GB" do
let(:provider_file_size) { 5368709121 }
it "should disable direct upload" do
expect(provider).to receive(:upload) do |**args|
expect(args[:direct]).to be_falsey
end
subject.upload_provider(org_name, box_name, box_version, box_version_provider, provider_file, access_token, options)
end
end
end end
end end

View File

@ -10,6 +10,7 @@ describe VagrantPlugins::CloudCommand::Command::Publish do
let(:account) { double("account") } let(:account) { double("account") }
let(:organization) { double("organization") } let(:organization) { double("organization") }
let(:box) { double("box") } let(:box) { double("box") }
let(:box_size) { 1 }
let(:version) { double("version") } let(:version) { double("version") }
let(:provider) { double("provider") } let(:provider) { double("provider") }
let(:uploader) { double("uploader") } let(:uploader) { double("uploader") }
@ -25,6 +26,8 @@ describe VagrantPlugins::CloudCommand::Command::Publish do
allow(ui).to receive(:success) allow(ui).to receive(:success)
allow(ui).to receive(:error) allow(ui).to receive(:error)
allow(iso_env).to receive(:ui).and_return(ui) allow(iso_env).to receive(:ui).and_return(ui)
allow(File).to receive(:stat).with(box).
and_return(double("box_stat", size: box_size))
allow(VagrantCloud::Account).to receive(:new). allow(VagrantCloud::Account).to receive(:new).
with(custom_server: anything, access_token: anything). with(custom_server: anything, access_token: anything).
and_return(account) and_return(account)
@ -60,10 +63,30 @@ describe VagrantPlugins::CloudCommand::Command::Publish do
subject.upload_box_file(provider, box) subject.upload_box_file(provider, box)
end end
it "should upload with PUT method when direct upload option set" do context "with direct upload option enabled" do
expect(Vagrant::Util::Uploader).to receive(:new). it "should upload with PUT method when direct upload option set" do
with(upload_url, anything, hash_including(method: :put)).and_return(uploader) expect(Vagrant::Util::Uploader).to receive(:new).
subject.upload_box_file(provider, box, direct_upload: true) with(upload_url, anything, hash_including(method: :put)).and_return(uploader)
subject.upload_box_file(provider, box, direct_upload: true)
end
context "with box size of 5GB" do
let(:box_size) { 5368709120 }
it "should upload using direct to storage option" do
expect(provider).to receive(:upload).with(direct: true)
subject.upload_box_file(provider, box, direct_upload: true)
end
end
context "with box size greater than 5GB" do
let(:box_size) { 5368709121 }
it "should disable direct to storage upload" do
expect(provider).to receive(:upload).with(direct: false)
subject.upload_box_file(provider, box, direct_upload: true)
end
end
end end
end end
@ -220,6 +243,8 @@ describe VagrantPlugins::CloudCommand::Command::Publish do
let(:client) { double("client", token: "1234token1234") } let(:client) { double("client", token: "1234token1234") }
let(:action_runner) { double("action_runner") } let(:action_runner) { double("action_runner") }
let(:box_path) { "path/to/the/virtualbox.box" } let(:box_path) { "path/to/the/virtualbox.box" }
let(:full_box_path) { "/full/#{box_path}" }
let(:box) { full_box_path }
before do before do
allow(iso_env).to receive(:action_runner). allow(iso_env).to receive(:action_runner).
@ -229,8 +254,10 @@ describe VagrantPlugins::CloudCommand::Command::Publish do
allow(subject).to receive(:format_box_results) allow(subject).to receive(:format_box_results)
allow(iso_env.ui).to receive(:ask).and_return("y") allow(iso_env.ui).to receive(:ask).and_return("y")
allow(File).to receive(:absolute_path).and_return("/full/#{box_path}") allow(File).to receive(:absolute_path).with(box_path)
allow(File).to receive(:file?).and_return(true) .and_return("/full/#{box_path}")
allow(File).to receive(:file?).with(box_path)
.and_return(true)
end end
context "with no arguments" do context "with no arguments" do