From 42718437ab7b9abb9133dcd2033fc3435b9f80cc Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Tue, 16 Jun 2020 10:43:51 -0700 Subject: [PATCH 1/7] Initial commit of cloud_init setup action This commit introduces a new builtin action for preparing and configuring guests cloud_init configs as an iso to be attached later by the disk actions --- lib/vagrant/action.rb | 1 + .../action/builtin/cloud_init_setup.rb | 124 +++++++++++++++++ lib/vagrant/errors.rb | 4 + plugins/providers/virtualbox/action.rb | 1 + templates/locales/en.yml | 5 + .../action/builtin/cloud_init_setup_test.rb | 126 ++++++++++++++++++ vagrant.gemspec | 11 +- 7 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 lib/vagrant/action/builtin/cloud_init_setup.rb create mode 100644 test/unit/vagrant/action/builtin/cloud_init_setup_test.rb diff --git a/lib/vagrant/action.rb b/lib/vagrant/action.rb index 31a910c04..95e7933c6 100644 --- a/lib/vagrant/action.rb +++ b/lib/vagrant/action.rb @@ -14,6 +14,7 @@ module Vagrant autoload :BoxRemove, "vagrant/action/builtin/box_remove" autoload :Call, "vagrant/action/builtin/call" autoload :CleanupDisks, "vagrant/action/builtin/cleanup_disks" + autoload :CloudInitSetup, "vagrant/action/builtin/cloud_init_setup" autoload :Confirm, "vagrant/action/builtin/confirm" autoload :ConfigValidate, "vagrant/action/builtin/config_validate" autoload :Delayed, "vagrant/action/builtin/delayed" diff --git a/lib/vagrant/action/builtin/cloud_init_setup.rb b/lib/vagrant/action/builtin/cloud_init_setup.rb new file mode 100644 index 000000000..e45488e38 --- /dev/null +++ b/lib/vagrant/action/builtin/cloud_init_setup.rb @@ -0,0 +1,124 @@ +require 'mime' +require "tmpdir" + +module Vagrant + module Action + module Builtin + class CloudInitSetup + TEMP_PREFIX = "vagrant-cloud-init-iso-temp-".freeze + TEMP_ROOT = "/tmp".freeze + + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant::action::builtin::cloudinit::setup") + end + + def call(env) + machine = env[:machine] + + user_data_configs = machine.config.vm.cloud_init_configs + .select { |c| c.type == :user_data } + + setup_user_data(machine, env, user_data_configs) + + # Continue On + @app.call(env) + end + + # @param [Vagrant::Machine] machine + # @param [Vagrant::Environment] env + # @param [Array<#VagrantPlugins::Kernel_V2::VagrantConfigCloudInit>] user_data_cfgs + def setup_user_data(machine, env, user_data_cfgs) + return if user_data_cfgs.empty? + + machine.ui.info(I18n.t("vagrant.actions.vm.cloud_init_user_data_setup")) + + text_cfgs = [] + user_data_cfgs.each do |cfg| + text_cfgs << read_text_cfg(machine, cfg) + end + + msg = generate_cfg_msg(machine, text_cfgs) + iso_path = write_cfg_iso(machine, env, msg) + + attach_disk_config(machine, env, iso_path) + end + + # Reads an individual cloud_init config and stores its contents and the + # content_type as a MIME text + # + # @param [Vagrant::Machine] machine + # @param [VagrantPlugins::Kernel_V2::VagrantConfigCloudInit] cfg + # @return [MIME::Text] text_msg + def read_text_cfg(machine, cfg) + if cfg.path + text = File.read(Pathname.new(cfg.path).expand_path(machine.env.root_path)) + else + text = cfg.inline + end + + # Note: content_type must remove the leading `text/` because + # the MIME::Text initializer hardcodes `text/` already to the type. + # We assume content_type is correct due to the validation step + # in VagrantConfigCloudInit. + content_type = cfg.content_type.split('/') + text_msg = MIME::Text.new(text, content_type[1]) + + text_msg + end + + # Combines all known cloud_init configs into a multipart mixed MIME text + # message + # + # @param [Vagrant::Machine] machine + # @param [Array] text_msg - One or more text configs + # @return [MIME::Multipart::Mixed] msg + def generate_cfg_msg(machine, text_cfgs) + msg = MIME::Multipart::Mixed.new + + text_cfgs.each do |c| + msg.add(c) + end + + msg + end + + # Writes the contents of the guests cloud_init config to a tmp + # dir and passes that source directory along to the host cap to be + # written to an iso + # + # @param [Vagrant::Machine] machine + # @param [MIME::Multipart::Mixed] msg + # @return [String] iso_path + def write_cfg_iso(machine, env, msg) + iso_path = nil + + if env[:env].host.capability?(:create_iso) + # TODO: make temp_root configurable? + source_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX, TEMP_ROOT)) + # write a cloud.cfg file with msg.to_s + File.open("#{source_dir}/cloud.cfg", 'w') { |file| file.write(msg.to_s) } + + iso_path = env[:env].host.capability(:create_iso, env[:env], source_dir) + else + raise Errors::CreateIsoHostCapNotFound + end + + iso_path + end + + # Adds a new :dvd disk config with the given iso_path to be attached + # to the guest later + # + # @param [Vagrant::Machine] machine + # @param [Vagrant::Environment] env + # @param [String] iso_path + def attach_disk_config(machine, env, iso_path) + @logger.info("Adding cloud_init iso '#{iso_path}' to disk config") + machine.config.vm.disk :dvd, file: iso_path, name: "vagrant-cloud_init-disk" + machine.config.vm.disks.map { |d| d.finalize! if d.type == :dvd && d.file == iso_path } + end + end + end + end +end diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 05e821bba..1d3a66799 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -340,6 +340,10 @@ module Vagrant error_key(:corrupt_machine_index) end + class CreateIsoHostCapNotFound < VagrantError + error_key(:create_iso_host_cap_not_found) + end + class DarwinMountFailed < VagrantError error_key(:darwin_mount_failed) end diff --git a/plugins/providers/virtualbox/action.rb b/plugins/providers/virtualbox/action.rb index 06c49d7d3..02198a076 100644 --- a/plugins/providers/virtualbox/action.rb +++ b/plugins/providers/virtualbox/action.rb @@ -79,6 +79,7 @@ module VagrantPlugins b.use ForwardPorts b.use SetHostname b.use SaneDefaults + b.use CloudInitSetup b.use CleanupDisks b.use Disk b.use Customize, "pre-boot" diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 985006e0d..5a50a5958 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -832,6 +832,9 @@ en: they'll have to be destroyed manually. Path: %{path} + create_iso_host_cap_not_found: |- + Vagrant cannot create an iso due to the host capability for creating isos not existing. + Vagrant will now exit. destroy_requires_force: |- Destroy doesn't have a TTY to ask for confirmation. Please pass the `--force` flag to force a destroy, otherwise attach a TTY so that @@ -2324,6 +2327,8 @@ en: create_master: failure: |- Failed to create lock-file for master VM creation for box %{box}. + cloud_init_user_data_setup: |- + Preparing cloud_init iso based on :user_data configuration... customize: failure: |- A customization command failed: diff --git a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb new file mode 100644 index 000000000..b3b26ca6d --- /dev/null +++ b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb @@ -0,0 +1,126 @@ +require File.expand_path("../../../../base", __FILE__) + +describe Vagrant::Action::Builtin::CloudInitSetup do + let(:app) { lambda { |env| } } + let(:vm) { double("vm", disk: disk, disks: disks) } + let(:disk) { double("disk") } + let(:disks) { double("disk") } + let(:config) { double("config", vm: vm) } + let(:provider) { double("provider") } + let(:machine) { double("machine", config: config, provider: provider, name: "machine", + provider_name: "provider", data_dir: Pathname.new("/fake/dir"), + ui: ui, env: machine_env) } + let(:host) { double("host") } + let(:machine_env) { double("machine_env", root_path: "root", host: host) } + let(:env) { { ui: ui, machine: machine, env: machine_env} } + + let(:ui) { double("ui", info: true, warn: true) } + + let(:cfg) { double("cfg", type: :user_data, content_type: "text/cloud-config", + path: "my/path", inline: nil) } + let(:cfg_inline) { double("cfg", type: :user_data, content_type: "text/cloud-config", + inline: "data: true", path: nil) } + let(:cloud_init_configs) { [cfg, cfg_inline] } + + let(:text_cfgs) { [MIME::Text.new("data: true", "cloud-config"), + MIME::Text.new("data: false", "cloud-config") ] } + + + let(:subject) { described_class.new(app, env) } + + describe "#call" do + it "calls setup_user_data if cloud_init config present" do + allow(vm).to receive(:cloud_init_configs).and_return(cloud_init_configs) + + expect(app).to receive(:call).with(env).ordered + + expect(subject).to receive(:setup_user_data).and_return(true) + + subject.call(env) + end + + it "continues on if no cloud_init config present" do + allow(vm).to receive(:cloud_init_configs).and_return([]) + + expect(app).to receive(:call).with(env).ordered + + expect(subject).not_to receive(:attack_disk_config) + + subject.call(env) + end + end + + describe "#setup_user_data" do + it "does nothing if passed in configs are empty" do + expect(subject).not_to receive(:read_text_cfg) + expect(subject).not_to receive(:generate_cfg_msg) + expect(subject).not_to receive(:write_cfg_iso) + expect(subject).not_to receive(:attach_disk_config) + subject.setup_user_data(machine, env, []) + end + + it "builds a MIME message and prepares a disc to be attached" do + expect(subject).to receive(:read_text_cfg).twice + + expect(subject).to receive(:generate_cfg_msg) + expect(subject).to receive(:write_cfg_iso) + expect(subject).to receive(:attach_disk_config) + + subject.setup_user_data(machine, env, cloud_init_configs) + end + end + + describe "#read_text_cfg" do + let(:cfg_text) { "config: true" } + + it "takes a text cfg path and saves it as a MIME text message" do + allow(File).to receive(:read).and_return(cfg_text) + + expect(MIME::Text).to receive(:new).with(cfg_text, "cloud-config") + subject.read_text_cfg(machine, cfg) + end + + it "takes a text cfg inline string and saves it as a MIME text message" do + expect(MIME::Text).to receive(:new).with("data: true", "cloud-config") + subject.read_text_cfg(machine, cfg_inline) + end + end + + describe "#generate_cfg_msg" do + it "creates a miltipart mixed message of combined configs" do + message = subject.generate_cfg_msg(machine, text_cfgs) + expect(message).to be_a(MIME::Multipart::Mixed) + end + end + + describe "#write_cfg_iso" do + let(:iso_path) { Pathname.new("fake/iso/path") } + let(:source_dir) { Pathname.new("fake/source/path") } + + it "raises an error if the host capability is not supported" do + message = subject.generate_cfg_msg(machine, text_cfgs) + allow(host).to receive(:capability?).with(:create_iso).and_return(false) + + expect{subject.write_cfg_iso(machine, env, message)}.to raise_error(Vagrant::Errors::CreateIsoHostCapNotFound) + end + + it "creates a temp dir with the cloud_init config and generates an iso" do + message = subject.generate_cfg_msg(machine, text_cfgs) + allow(host).to receive(:capability?).with(:create_iso).and_return(true) + allow(Dir).to receive(:mktmpdir).and_return(source_dir) + expect(File).to receive(:open).with("#{source_dir}/cloud.cfg", 'w').and_return(true) + allow(host).to receive(:capability).with(:create_iso, machine_env, source_dir).and_return(iso_path) + + subject.write_cfg_iso(machine, env, message) + end + end + + describe "#attach_disk_config" do + let(:iso_path) { Pathname.new("fake/iso/path") } + + it "creates a new disk config based on the iso_path" do + expect(vm.disks).to receive(:map) + subject.attach_disk_config(machine, env, iso_path) + end + end +end diff --git a/vagrant.gemspec b/vagrant.gemspec index 8667a36d4..a13f66def 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -19,21 +19,22 @@ Gem::Specification.new do |s| s.add_dependency "childprocess", "~> 3.0.0" s.add_dependency "ed25519", "~> 1.2.4" s.add_dependency "erubis", "~> 2.7.0" + s.add_dependency "hashicorp-checkpoint", "~> 0.1.5" s.add_dependency "i18n", "~> 1.8" s.add_dependency "listen", "~> 3.1.5" - s.add_dependency "hashicorp-checkpoint", "~> 0.1.5" s.add_dependency "log4r", "~> 1.1.9", "< 1.1.11" - s.add_dependency "net-ssh", "~> 5.2.0" - s.add_dependency "net-sftp", "~> 2.1" + s.add_dependency "mime", "~> 0.4.4" s.add_dependency "net-scp", "~> 1.2.0" + s.add_dependency "net-sftp", "~> 2.1" + s.add_dependency "net-ssh", "~> 5.2.0" s.add_dependency "rb-kqueue", "~> 0.2.0" s.add_dependency "rest-client", ">= 1.6.0", "< 3.0" s.add_dependency "rubyzip", "~> 2.0" + s.add_dependency "vagrant_cloud", "~> 2.0.3" s.add_dependency "wdm", "~> 0.1.0" s.add_dependency "winrm", ">= 2.3.4", "< 3.0" - s.add_dependency "winrm-fs", ">= 1.3.4", "< 2.0" s.add_dependency "winrm-elevated", ">= 1.2.1", "< 2.0" - s.add_dependency "vagrant_cloud", "~> 2.0.3" + s.add_dependency "winrm-fs", ">= 1.3.4", "< 2.0" # NOTE: The ruby_dep gem is an implicit dependency from the listen gem. Later versions # of the ruby_dep gem impose an aggressive constraint on the required ruby version (>= 2.2.5). From 6797b96caef5cd6c6a219ffe35a720f4758f998e Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 18 Jun 2020 10:42:07 -0700 Subject: [PATCH 2/7] Save file as user-data --- lib/vagrant/action/builtin/cloud_init_setup.rb | 2 +- test/unit/vagrant/action/builtin/cloud_init_setup_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vagrant/action/builtin/cloud_init_setup.rb b/lib/vagrant/action/builtin/cloud_init_setup.rb index e45488e38..58382ed20 100644 --- a/lib/vagrant/action/builtin/cloud_init_setup.rb +++ b/lib/vagrant/action/builtin/cloud_init_setup.rb @@ -97,7 +97,7 @@ module Vagrant # TODO: make temp_root configurable? source_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX, TEMP_ROOT)) # write a cloud.cfg file with msg.to_s - File.open("#{source_dir}/cloud.cfg", 'w') { |file| file.write(msg.to_s) } + File.open("#{source_dir}/user-data", 'w') { |file| file.write(msg.to_s) } iso_path = env[:env].host.capability(:create_iso, env[:env], source_dir) else diff --git a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb index b3b26ca6d..81602bca1 100644 --- a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb +++ b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb @@ -108,7 +108,7 @@ describe Vagrant::Action::Builtin::CloudInitSetup do message = subject.generate_cfg_msg(machine, text_cfgs) allow(host).to receive(:capability?).with(:create_iso).and_return(true) allow(Dir).to receive(:mktmpdir).and_return(source_dir) - expect(File).to receive(:open).with("#{source_dir}/cloud.cfg", 'w').and_return(true) + expect(File).to receive(:open).with("#{source_dir}/user-data", 'w').and_return(true) allow(host).to receive(:capability).with(:create_iso, machine_env, source_dir).and_return(iso_path) subject.write_cfg_iso(machine, env, message) From 39a12de38f3bdb7f89f696665882dc337d7296f0 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 18 Jun 2020 11:07:56 -0700 Subject: [PATCH 3/7] Ensure meta-data is also included in cloud-init iso --- lib/vagrant/action/builtin/cloud_init_setup.rb | 18 +++++++++++------- .../action/builtin/cloud_init_setup_test.rb | 4 +++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/vagrant/action/builtin/cloud_init_setup.rb b/lib/vagrant/action/builtin/cloud_init_setup.rb index 58382ed20..dad5665b6 100644 --- a/lib/vagrant/action/builtin/cloud_init_setup.rb +++ b/lib/vagrant/action/builtin/cloud_init_setup.rb @@ -1,12 +1,11 @@ require 'mime' -require "tmpdir" +require 'tmpdir' module Vagrant module Action module Builtin class CloudInitSetup TEMP_PREFIX = "vagrant-cloud-init-iso-temp-".freeze - TEMP_ROOT = "/tmp".freeze def initialize(app, env) @app = app @@ -94,12 +93,17 @@ module Vagrant iso_path = nil if env[:env].host.capability?(:create_iso) - # TODO: make temp_root configurable? - source_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX, TEMP_ROOT)) - # write a cloud.cfg file with msg.to_s - File.open("#{source_dir}/user-data", 'w') { |file| file.write(msg.to_s) } + begin + source_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX)) + File.open("#{source_dir}/user-data", 'w') { |file| file.write(msg.to_s) } - iso_path = env[:env].host.capability(:create_iso, env[:env], source_dir) + metadata = { "instance-id": "i-#{machine.id.split('-').join}" } + File.open("#{source_dir}/meta-data", 'w') { |file| file.write(metadata.to_s) } + + iso_path = env[:env].host.capability(:create_iso, env[:env], source_dir) + ensure + FileUtils.remove_entry source_dir + end else raise Errors::CreateIsoHostCapNotFound end diff --git a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb index 81602bca1..00628e97a 100644 --- a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb +++ b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb @@ -9,7 +9,7 @@ describe Vagrant::Action::Builtin::CloudInitSetup do let(:provider) { double("provider") } let(:machine) { double("machine", config: config, provider: provider, name: "machine", provider_name: "provider", data_dir: Pathname.new("/fake/dir"), - ui: ui, env: machine_env) } + ui: ui, env: machine_env, id: "123-456-789") } let(:host) { double("host") } let(:machine_env) { double("machine_env", root_path: "root", host: host) } let(:env) { { ui: ui, machine: machine, env: machine_env} } @@ -109,6 +109,8 @@ describe Vagrant::Action::Builtin::CloudInitSetup do allow(host).to receive(:capability?).with(:create_iso).and_return(true) allow(Dir).to receive(:mktmpdir).and_return(source_dir) expect(File).to receive(:open).with("#{source_dir}/user-data", 'w').and_return(true) + expect(File).to receive(:open).with("#{source_dir}/meta-data", 'w').and_return(true) + expect(FileUtils).to receive(:remove_entry).with(source_dir).and_return(true) allow(host).to receive(:capability).with(:create_iso, machine_env, source_dir).and_return(iso_path) subject.write_cfg_iso(machine, env, message) From d6fc70dd68a550ebb4f72a7a2298c5dabb07c06a Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 18 Jun 2020 11:09:21 -0700 Subject: [PATCH 4/7] Include volume_id in iso host cap params --- lib/vagrant/action/builtin/cloud_init_setup.rb | 3 ++- test/unit/vagrant/action/builtin/cloud_init_setup_test.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/vagrant/action/builtin/cloud_init_setup.rb b/lib/vagrant/action/builtin/cloud_init_setup.rb index dad5665b6..e44309b6f 100644 --- a/lib/vagrant/action/builtin/cloud_init_setup.rb +++ b/lib/vagrant/action/builtin/cloud_init_setup.rb @@ -100,7 +100,8 @@ module Vagrant metadata = { "instance-id": "i-#{machine.id.split('-').join}" } File.open("#{source_dir}/meta-data", 'w') { |file| file.write(metadata.to_s) } - iso_path = env[:env].host.capability(:create_iso, env[:env], source_dir) + iso_path = env[:env].host.capability(:create_iso, env[:env], + source_dir, volume_id: "cidata") ensure FileUtils.remove_entry source_dir end diff --git a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb index 00628e97a..3e2911b6a 100644 --- a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb +++ b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb @@ -111,7 +111,7 @@ describe Vagrant::Action::Builtin::CloudInitSetup do expect(File).to receive(:open).with("#{source_dir}/user-data", 'w').and_return(true) expect(File).to receive(:open).with("#{source_dir}/meta-data", 'w').and_return(true) expect(FileUtils).to receive(:remove_entry).with(source_dir).and_return(true) - allow(host).to receive(:capability).with(:create_iso, machine_env, source_dir).and_return(iso_path) + allow(host).to receive(:capability).with(:create_iso, machine_env, source_dir, volume_id: "cidata").and_return(iso_path) subject.write_cfg_iso(machine, env, message) end From 3caca048c8907ef9766b476a0ca8619fbdf0220f Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 18 Jun 2020 13:41:08 -0700 Subject: [PATCH 5/7] Update setup to decouple disk attachment from user_data setup --- .../action/builtin/cloud_init_setup.rb | 27 ++++++++++--------- .../action/builtin/cloud_init_setup_test.rb | 20 ++++++-------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/vagrant/action/builtin/cloud_init_setup.rb b/lib/vagrant/action/builtin/cloud_init_setup.rb index e44309b6f..bfe785ff4 100644 --- a/lib/vagrant/action/builtin/cloud_init_setup.rb +++ b/lib/vagrant/action/builtin/cloud_init_setup.rb @@ -18,7 +18,12 @@ module Vagrant user_data_configs = machine.config.vm.cloud_init_configs .select { |c| c.type == :user_data } - setup_user_data(machine, env, user_data_configs) + if !user_data_configs.empty? + user_data = setup_user_data(machine, env, user_data_configs) + meta_data = { "instance-id": "i-#{machine.id.split('-').join}" } + + write_cfg_iso(machine, env, user_data, meta_data) + end # Continue On @app.call(env) @@ -27,9 +32,8 @@ module Vagrant # @param [Vagrant::Machine] machine # @param [Vagrant::Environment] env # @param [Array<#VagrantPlugins::Kernel_V2::VagrantConfigCloudInit>] user_data_cfgs + # @return [MIME::Text] user_data def setup_user_data(machine, env, user_data_cfgs) - return if user_data_cfgs.empty? - machine.ui.info(I18n.t("vagrant.actions.vm.cloud_init_user_data_setup")) text_cfgs = [] @@ -37,10 +41,8 @@ module Vagrant text_cfgs << read_text_cfg(machine, cfg) end - msg = generate_cfg_msg(machine, text_cfgs) - iso_path = write_cfg_iso(machine, env, msg) - - attach_disk_config(machine, env, iso_path) + user_data = generate_cfg_msg(machine, text_cfgs) + user_data end # Reads an individual cloud_init config and stores its contents and the @@ -89,27 +91,26 @@ module Vagrant # @param [Vagrant::Machine] machine # @param [MIME::Multipart::Mixed] msg # @return [String] iso_path - def write_cfg_iso(machine, env, msg) + def write_cfg_iso(machine, env, user_data, meta_data) iso_path = nil if env[:env].host.capability?(:create_iso) begin source_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX)) - File.open("#{source_dir}/user-data", 'w') { |file| file.write(msg.to_s) } + File.open("#{source_dir}/user-data", 'w') { |file| file.write(user_data.to_s) } - metadata = { "instance-id": "i-#{machine.id.split('-').join}" } - File.open("#{source_dir}/meta-data", 'w') { |file| file.write(metadata.to_s) } + File.open("#{source_dir}/meta-data", 'w') { |file| file.write(meta_data.to_s) } iso_path = env[:env].host.capability(:create_iso, env[:env], source_dir, volume_id: "cidata") + + attach_disk_config(machine, env, iso_path) ensure FileUtils.remove_entry source_dir end else raise Errors::CreateIsoHostCapNotFound end - - iso_path end # Adds a new :dvd disk config with the given iso_path to be attached diff --git a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb index 3e2911b6a..d2856e12b 100644 --- a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb +++ b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb @@ -25,6 +25,8 @@ describe Vagrant::Action::Builtin::CloudInitSetup do let(:text_cfgs) { [MIME::Text.new("data: true", "cloud-config"), MIME::Text.new("data: false", "cloud-config") ] } + let(:meta_data) { { "instance-id": "i-123456789" } } + let(:subject) { described_class.new(app, env) } @@ -35,6 +37,7 @@ describe Vagrant::Action::Builtin::CloudInitSetup do expect(app).to receive(:call).with(env).ordered expect(subject).to receive(:setup_user_data).and_return(true) + expect(subject).to receive(:write_cfg_iso).and_return(true) subject.call(env) end @@ -44,6 +47,8 @@ describe Vagrant::Action::Builtin::CloudInitSetup do expect(app).to receive(:call).with(env).ordered + expect(subject).not_to receive(:setup_user_data) + expect(subject).not_to receive(:write_cfg_iso) expect(subject).not_to receive(:attack_disk_config) subject.call(env) @@ -51,20 +56,10 @@ describe Vagrant::Action::Builtin::CloudInitSetup do end describe "#setup_user_data" do - it "does nothing if passed in configs are empty" do - expect(subject).not_to receive(:read_text_cfg) - expect(subject).not_to receive(:generate_cfg_msg) - expect(subject).not_to receive(:write_cfg_iso) - expect(subject).not_to receive(:attach_disk_config) - subject.setup_user_data(machine, env, []) - end - it "builds a MIME message and prepares a disc to be attached" do expect(subject).to receive(:read_text_cfg).twice expect(subject).to receive(:generate_cfg_msg) - expect(subject).to receive(:write_cfg_iso) - expect(subject).to receive(:attach_disk_config) subject.setup_user_data(machine, env, cloud_init_configs) end @@ -101,7 +96,7 @@ describe Vagrant::Action::Builtin::CloudInitSetup do message = subject.generate_cfg_msg(machine, text_cfgs) allow(host).to receive(:capability?).with(:create_iso).and_return(false) - expect{subject.write_cfg_iso(machine, env, message)}.to raise_error(Vagrant::Errors::CreateIsoHostCapNotFound) + expect{subject.write_cfg_iso(machine, env, message, {})}.to raise_error(Vagrant::Errors::CreateIsoHostCapNotFound) end it "creates a temp dir with the cloud_init config and generates an iso" do @@ -112,8 +107,9 @@ describe Vagrant::Action::Builtin::CloudInitSetup do expect(File).to receive(:open).with("#{source_dir}/meta-data", 'w').and_return(true) expect(FileUtils).to receive(:remove_entry).with(source_dir).and_return(true) allow(host).to receive(:capability).with(:create_iso, machine_env, source_dir, volume_id: "cidata").and_return(iso_path) + expect(vm.disks).to receive(:map) - subject.write_cfg_iso(machine, env, message) + subject.write_cfg_iso(machine, env, message, {}) end end From 4b15435262cc0f78f0c2045437100a35afad3998 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 18 Jun 2020 13:46:06 -0700 Subject: [PATCH 6/7] Fix method docs --- lib/vagrant/action/builtin/cloud_init_setup.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vagrant/action/builtin/cloud_init_setup.rb b/lib/vagrant/action/builtin/cloud_init_setup.rb index bfe785ff4..7a405a46b 100644 --- a/lib/vagrant/action/builtin/cloud_init_setup.rb +++ b/lib/vagrant/action/builtin/cloud_init_setup.rb @@ -89,8 +89,8 @@ module Vagrant # written to an iso # # @param [Vagrant::Machine] machine - # @param [MIME::Multipart::Mixed] msg - # @return [String] iso_path + # @param [MIME::Multipart::Mixed] user_data + # @param [Hash] meta_data def write_cfg_iso(machine, env, user_data, meta_data) iso_path = nil From 39c3ee7230e02a38235ef61d13978e7c0ab02141 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 29 Jun 2020 11:16:55 -0700 Subject: [PATCH 7/7] Update code style for map/each methods --- lib/vagrant/action/builtin/cloud_init_setup.rb | 13 +++++-------- .../vagrant/action/builtin/cloud_init_setup_test.rb | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/vagrant/action/builtin/cloud_init_setup.rb b/lib/vagrant/action/builtin/cloud_init_setup.rb index 7a405a46b..6674eb05d 100644 --- a/lib/vagrant/action/builtin/cloud_init_setup.rb +++ b/lib/vagrant/action/builtin/cloud_init_setup.rb @@ -36,10 +36,7 @@ module Vagrant def setup_user_data(machine, env, user_data_cfgs) machine.ui.info(I18n.t("vagrant.actions.vm.cloud_init_user_data_setup")) - text_cfgs = [] - user_data_cfgs.each do |cfg| - text_cfgs << read_text_cfg(machine, cfg) - end + text_cfgs = user_data_cfgs.map { |cfg| read_text_cfg(machine, cfg) } user_data = generate_cfg_msg(machine, text_cfgs) user_data @@ -62,8 +59,8 @@ module Vagrant # the MIME::Text initializer hardcodes `text/` already to the type. # We assume content_type is correct due to the validation step # in VagrantConfigCloudInit. - content_type = cfg.content_type.split('/') - text_msg = MIME::Text.new(text, content_type[1]) + content_type = cfg.content_type.split('/', 2).last + text_msg = MIME::Text.new(text, content_type) text_msg end @@ -106,7 +103,7 @@ module Vagrant attach_disk_config(machine, env, iso_path) ensure - FileUtils.remove_entry source_dir + FileUtils.remove_entry(source_dir) end else raise Errors::CreateIsoHostCapNotFound @@ -122,7 +119,7 @@ module Vagrant def attach_disk_config(machine, env, iso_path) @logger.info("Adding cloud_init iso '#{iso_path}' to disk config") machine.config.vm.disk :dvd, file: iso_path, name: "vagrant-cloud_init-disk" - machine.config.vm.disks.map { |d| d.finalize! if d.type == :dvd && d.file == iso_path } + machine.config.vm.disks.each { |d| d.finalize! if d.type == :dvd && d.file == iso_path } end end end diff --git a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb index d2856e12b..f5fb89f48 100644 --- a/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb +++ b/test/unit/vagrant/action/builtin/cloud_init_setup_test.rb @@ -107,7 +107,7 @@ describe Vagrant::Action::Builtin::CloudInitSetup do expect(File).to receive(:open).with("#{source_dir}/meta-data", 'w').and_return(true) expect(FileUtils).to receive(:remove_entry).with(source_dir).and_return(true) allow(host).to receive(:capability).with(:create_iso, machine_env, source_dir, volume_id: "cidata").and_return(iso_path) - expect(vm.disks).to receive(:map) + expect(vm.disks).to receive(:each) subject.write_cfg_iso(machine, env, message, {}) end @@ -117,7 +117,7 @@ describe Vagrant::Action::Builtin::CloudInitSetup do let(:iso_path) { Pathname.new("fake/iso/path") } it "creates a new disk config based on the iso_path" do - expect(vm.disks).to receive(:map) + expect(vm.disks).to receive(:each) subject.attach_disk_config(machine, env, iso_path) end end