128 lines
4.6 KiB
Ruby
128 lines
4.6 KiB
Ruby
require 'mime'
|
|
require 'tmpdir'
|
|
|
|
module Vagrant
|
|
module Action
|
|
module Builtin
|
|
class CloudInitSetup
|
|
TEMP_PREFIX = "vagrant-cloud-init-iso-temp-".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 }
|
|
|
|
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)
|
|
end
|
|
|
|
# @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)
|
|
machine.ui.info(I18n.t("vagrant.actions.vm.cloud_init_user_data_setup"))
|
|
|
|
text_cfgs = user_data_cfgs.map { |cfg| read_text_cfg(machine, cfg) }
|
|
|
|
user_data = generate_cfg_msg(machine, text_cfgs)
|
|
user_data
|
|
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('/', 2).last
|
|
text_msg = MIME::Text.new(text, content_type)
|
|
|
|
text_msg
|
|
end
|
|
|
|
# Combines all known cloud_init configs into a multipart mixed MIME text
|
|
# message
|
|
#
|
|
# @param [Vagrant::Machine] machine
|
|
# @param [Array<MIME::Text>] text_msg - One or more text configs
|
|
# @return [MIME::Multipart::Mixed] msg
|
|
def generate_cfg_msg(machine, text_cfgs)
|
|
msg = MIME::Multipart::Mixed.new
|
|
msg.headers.set("MIME-Version", "1.0")
|
|
|
|
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] user_data
|
|
# @param [Hash] meta_data
|
|
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(user_data.to_s) }
|
|
|
|
File.open("#{source_dir}/meta-data", 'w') { |file| file.write(meta_data.to_yaml) }
|
|
|
|
iso_path = env[:env].host.capability(:create_iso,
|
|
source_dir, volume_id: "cidata")
|
|
attach_disk_config(machine, env, iso_path.to_path)
|
|
ensure
|
|
FileUtils.remove_entry(source_dir)
|
|
end
|
|
else
|
|
raise Errors::CreateIsoHostCapNotFound
|
|
end
|
|
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.each { |d| d.finalize! if d.type == :dvd && d.file == iso_path }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|