From e1d104a8e3cf4c50b22afd62286e32ce79d99aa7 Mon Sep 17 00:00:00 2001 From: sophia Date: Wed, 10 Jun 2020 15:53:41 -0500 Subject: [PATCH] Build iso for Darwin host --- lib/vagrant/errors.rb | 4 ++ plugins/hosts/darwin/cap/fs_iso.rb | 35 ++++++++--- plugins/kernel_v2/config/cloud_init.rb | 50 --------------- plugins/kernel_v2/plugin.rb | 11 +--- templates/locales/en.yml | 12 ++++ .../plugins/hosts/darwin/cap/fs_iso_test.rb | 61 +++++++++++++++++++ 6 files changed, 105 insertions(+), 68 deletions(-) delete mode 100644 plugins/kernel_v2/config/cloud_init.rb create mode 100644 test/unit/plugins/hosts/darwin/cap/fs_iso_test.rb diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 05e821bba..d2c137af0 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -428,6 +428,10 @@ module Vagrant error_key(:host_explicit_not_detected) end + class ISOBuildFailed < VagrantError + error_key(:iso_build_failed) + end + class LinuxMountFailed < VagrantError error_key(:linux_mount_failed) end diff --git a/plugins/hosts/darwin/cap/fs_iso.rb b/plugins/hosts/darwin/cap/fs_iso.rb index 8a2d858eb..07c0e853f 100644 --- a/plugins/hosts/darwin/cap/fs_iso.rb +++ b/plugins/hosts/darwin/cap/fs_iso.rb @@ -1,4 +1,8 @@ require "tempfile" +require 'fileutils' +require 'pathname' +require "vagrant/util/subprocess" +require "vagrant/util/map_command_options" module VagrantPlugins module HostDarwin @@ -6,12 +10,14 @@ module VagrantPlugins class FsISO @@logger = Log4r::Logger.new("vagrant::host::darwin::fs_iso") + BUILD_ISO_CMD = "hdiutil".freeze + # Check that the host has the ability to generate ISOs # # @param [Vagrant::Environment] env # @return [Boolean] def self.isofs_available(env) - !!Vagrant::Util::Which.which("hdiutil") + !!Vagrant::Util::Which.which(BUILD_ISO_CMD) end # Generate an ISO file of the given source directory @@ -19,26 +25,34 @@ module VagrantPlugins # @param [Vagrant::Environment] env # @param [String, Pathname] source_directory Contents of ISO # @param [String, Pathname, nil] file_destination Location to store ISO + # @param [Map] extra arguments to pass to the iso building command # @return [Pathname] ISO location # @note If file_destination exists, source_directory will be checked # for recent modifications and a new ISO will be generated if requried. - def self.create_iso(env, source_directory, file_destination=nil) + def self.create_iso(env, source_directory, file_destination=nil, extra_opts={}) if file_destination.nil? tmpfile = Tempfile.new("vagrant-iso") file_destination = Pathname.new(tmpfile.path) tmpfile.delete else file_destination = Pathname.new(file_destination.to_s) - end - source_directory = Pathname.new(source_directory) - if iso_update_required?(file_destination, source_directory) # Ensure destination directory is available FileUtils.mkdir_p(file_destination.to_s) - result = Vagrant::Util::Subprocess.execute("hdiutil", "makehybrid", "-o", - file_destination.to_s, "-hfs", "-joliet", "-iso", "-default-volume-name", - "cidata", source_directory.to_s) + end + source_directory = Pathname.new(source_directory) + if iso_update_required?(file_destination, source_directory) + iso_command = [BUILD_ISO_CMD, "makehybrid"] + iso_command << "-hfs" + iso_command << "-iso" + iso_command << "-joliet" + iso_command.concat(Vagrant::Util::MapCommandOptions.map_to_command_options(extra_opts, cmd_flag="-")) + iso_command << "-o" + iso_command << file_destination.to_s + iso_command << source_directory.to_s + result = Vagrant::Util::Subprocess.execute(*iso_command) + if result.exit_code != 0 - raise "Failed to create ISO!" + raise Vagrant::Errors::ISOBuildFailed, cmd: iso_command.join(" "), stdout: result.stdout, stderr: result.stderr end end file_destination @@ -51,10 +65,11 @@ module VagrantPlugins # @return [Boolean] def self.iso_update_required?(iso_path, dir_path) Dir.glob(dir_path.join("**/**/*")).each do |path| - if File.mtime > iso_path.mtime + if Pathname.new(path).mtime > iso_path.mtime return true end end + @@logger.info("ISO update not required! No changes found in source path #{dir_path}") false end end diff --git a/plugins/kernel_v2/config/cloud_init.rb b/plugins/kernel_v2/config/cloud_init.rb deleted file mode 100644 index 271e9df83..000000000 --- a/plugins/kernel_v2/config/cloud_init.rb +++ /dev/null @@ -1,50 +0,0 @@ -require "vagrant" - -module VagrantPlugins - module Kernel_V2 - class CloudInit < Vagrant.plugin("2", :config) - - DEFAULT_SOURCE_PATTERN = "**/**/*".freeze - - attr_accessor :iso_path - attr_accessor :source_files_pattern - attr_accessor :source_directory - - def initialize - @iso_path = UNSET_VALUE - @source_files_pattern = UNSET_VALUE - @source_directory = UNSET_VALUE - end - - def finalize! - if @iso_path != UNSET_VALUE - @iso_path = Pathname.new(@iso_path.to_s) - else - @iso_path = nil - end - if @source_files_pattern == UNSET_VALUE - @source_files_pattern = DEFAULT_SOURCE_PATTERN - end - if @source_directory != UNSET_VALUE - @source_directory = Pathname.new(@source_directory.to_s) - else - @source_directory = nil - end - end - - def validate(machine) - errors = _detected_errors - if @source_directory.nil? || !@source_directory.exist? - errors << I18n.t("vagrant.config.cloud_init.invalid_source_directory", - directory: source_directory.to_s) - end - - {"cloud_init" => errors} - end - - def to_s - "CloudInit" - end - end - end -end diff --git a/plugins/kernel_v2/plugin.rb b/plugins/kernel_v2/plugin.rb index 846106828..280470d19 100644 --- a/plugins/kernel_v2/plugin.rb +++ b/plugins/kernel_v2/plugin.rb @@ -15,9 +15,9 @@ module VagrantPlugins # "kernel_v1", none of these configuration classes are upgradable. # This is by design, since we can't be sure if they're upgradable # until another version is available. - config("cloud_init") do - require File.expand_path("../config/cloud_init", __FILE__) - CloudInit + config("ssh") do + require File.expand_path("../config/ssh", __FILE__) + SSHConfig end config("package") do @@ -30,11 +30,6 @@ module VagrantPlugins PushConfig end - config("ssh") do - require File.expand_path("../config/ssh", __FILE__) - SSHConfig - end - config("vagrant") do require File.expand_path("../config/vagrant", __FILE__) VagrantConfig diff --git a/templates/locales/en.yml b/templates/locales/en.yml index a6f8feea9..6a443055f 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -939,6 +939,18 @@ en: ("%{value}") could not be found. Please verify that the plugin is installed which implements this host and that the value you used for `config.vagrant.host` is correct. + iso_build_failed: |- + Failed to build iso image. The following command returned an error: + + %{cmd} + + Stdout from the command: + + %{stdout} + + Stderr from the command: + + %{stderr} hyperv_virtualbox_error: |- Hyper-V and VirtualBox cannot be used together and will result in a system crash. Vagrant will now exit. Please disable Hyper-V if you wish diff --git a/test/unit/plugins/hosts/darwin/cap/fs_iso_test.rb b/test/unit/plugins/hosts/darwin/cap/fs_iso_test.rb new file mode 100644 index 000000000..08d5eb564 --- /dev/null +++ b/test/unit/plugins/hosts/darwin/cap/fs_iso_test.rb @@ -0,0 +1,61 @@ +require 'pathname' + +require_relative "../../../../base" +require_relative "../../../../../../plugins/hosts/darwin/cap/fs_iso" + +describe VagrantPlugins::HostDarwin::Cap::FsISO do + include_context "unit" + + let(:subject){ VagrantPlugins::HostDarwin::Cap::FsISO } + + let(:env){ double("env") } + + describe ".isofs_available" do + it "finds iso building utility when available" do + expect(Vagrant::Util::Which).to receive(:which).and_return(true) + expect(subject.isofs_available(env)).to eq(true) + end + + it "does not find iso building utility when not available" do + expect(Vagrant::Util::Which).to receive(:which).and_return(false) + expect(subject.isofs_available(env)).to eq(false) + end + end + + describe ".create_iso" do + before do + allow(subject).to receive(:iso_update_required?).and_return(true) + allow(FileUtils).to receive(:mkdir_p) + end + + it "builds an iso" do + expect(Vagrant::Util::Subprocess).to receive(:execute).with( + "hdiutil", "makehybrid", "-hfs", "-iso", "-joliet", "-o", /.iso/, /\/foo\/src/ + ).and_return(double(exit_code: 0)) + + output = subject.create_iso(env, "/foo/src", "/woo/out.iso") + expect(output.to_s).to eq("/woo/out.iso") + end + + it "builds an iso with args" do + expect(Vagrant::Util::Subprocess).to receive(:execute).with( + "hdiutil", "makehybrid", "-hfs", "-iso", "-joliet", "-default-volume-name", "cidata", "-o", /.iso/, /\/foo\/src/ + ).and_return(double(exit_code: 0)) + + output = subject.create_iso(env, "/foo/src", "/woo/out.iso", extra_opts={"default-volume-name" => "cidata"}) + expect(output.to_s).to eq("/woo/out.iso") + end + + it "raises an error if iso build failed" do + allow(Vagrant::Util::Subprocess).to receive(:execute).with(any_args).and_return(double(stdout: "nope", stderr: "nope", exit_code: 1)) + expect{ subject.create_iso(env, "/foo/src", "/woo/out.iso") }.to raise_error(Vagrant::Errors::ISOBuildFailed) + end + + it "does not build iso if no changes required" do + allow(subject).to receive(:iso_update_required?).and_return(false) + expect(Vagrant::Util::Subprocess).to_not receive(:execute) + output = subject.create_iso(env, "/foo/src", "/woo/out.iso", extra_opts={"default-volume-name" => "cidata"}) + expect(output.to_s).to eq("/woo/out.iso") + end + end +end