Class: Vagrant::Action::Builtin::CloudInitSetup

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant/action/builtin/cloud_init_setup.rb

Constant Summary collapse

TEMP_PREFIX =
"vagrant-cloud-init-iso-temp-".freeze

Instance Method Summary collapse

Constructor Details

#initialize(app, env) ⇒ CloudInitSetup

Returns a new instance of CloudInitSetup.



13
14
15
16
# File 'lib/vagrant/action/builtin/cloud_init_setup.rb', line 13

def initialize(app, env)
  @app    = app
  @logger = Log4r::Logger.new("vagrant::action::builtin::cloudinit::setup")
end

Instance Method Details

#attach_disk_config(machine, env, iso_path) ⇒ Object

Adds a new :dvd disk config with the given iso_path to be attached to the guest later

Parameters:



158
159
160
161
162
# File 'lib/vagrant/action/builtin/cloud_init_setup.rb', line 158

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

#call(env) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/vagrant/action/builtin/cloud_init_setup.rb', line 18

def call(env)
  catch(:complete) do
    machine = env[:machine]

    # The sentinel file in this check is written by the cloud init
    # wait action and is only written after cloud init has completed.
    @logger.info("Checking cloud-init sentinel file...")
    sentinel_path = machine.data_dir.join("action_cloud_init")
    if sentinel_path.file?
      contents = sentinel_path.read.chomp
      if machine.id.to_s == contents
        if machine.config.vm.cloud_init_first_boot_only
          @logger.info("Sentinel found for cloud-init, skipping")
          throw :complete
        else
          @logger.info("Sentinel found for cloud-init but is configuration enabled")
        end
      else
        @logger.debug("Found stale sentinel file, removing... (#{machine.id} != #{contents})")
      end
      sentinel_path.unlink
    end

    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)
       = { "instance-id" => "i-#{machine.id.split('-').join}" }

      write_cfg_iso(machine, env, user_data, )
    end
  end

  # Continue On
  @app.call(env)
end

#generate_cfg_msg(machine, text_cfgs) ⇒ Vagrant::Util::Mime::Multipart

Combines all known cloud_init configs into a multipart mixed MIME text message

Parameters:

Returns:



94
95
96
97
98
99
100
101
102
103
# File 'lib/vagrant/action/builtin/cloud_init_setup.rb', line 94

def generate_cfg_msg(machine, text_cfgs)
  msg = Vagrant::Util::Mime::Multipart.new
  msg.headers["MIME-Version"] = "1.0"

  text_cfgs.each do |c|
    msg.add(c)
  end

  msg
end

#read_text_cfg(machine, cfg) ⇒ Vagrant::Util::Mime::Entity

Reads an individual cloud_init config and stores its contents and the content_type as a MIME text

Parameters:

  • machine (Vagrant::Machine)
  • cfg (VagrantPlugins::Kernel_V2::VagrantConfigCloudInit)

Returns:



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/vagrant/action/builtin/cloud_init_setup.rb', line 76

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

  text_msg = Vagrant::Util::Mime::Entity.new(text, cfg.content_type)
  text_msg.disposition = "attachment; filename=\"#{File.basename(cfg.content_disposition_filename).gsub('"', '\"')}\"" if cfg.content_disposition_filename
  text_msg
end

#setup_user_data(machine, env, user_data_cfgs) ⇒ Vagrant::Util::Mime::MultiPart

Returns user_data.

Parameters:

Returns:

  • (Vagrant::Util::Mime::MultiPart)

    user_data



61
62
63
64
65
66
67
68
# File 'lib/vagrant/action/builtin/cloud_init_setup.rb', line 61

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

#write_cfg_iso(machine, env, user_data, meta_data) ⇒ Object

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

Parameters:

Raises:



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/vagrant/action/builtin/cloud_init_setup.rb', line 112

def write_cfg_iso(machine, env, user_data, )
  raise Errors::CreateIsoHostCapNotFound if !env[:env].host.capability?(:create_iso)

  iso_path = catch(:iso_path) do
    # This iso sentinel file is used to store the path of the
    # generated iso file and its checksum. If the file does
    # not exist, or the actual checksum of the file does not
    # match that stored in the sentinel file, it is ignored
    # and the iso is generated. This is used to prevent multiple
    # iso file from being created over time.
    iso_sentinel = env[:machine].data_dir.join("action_cloud_init_iso")
    if iso_sentinel.file?
      checksum, path = iso_sentinel.read.chomp.split(":", 2)
      if File.exist?(path) && Vagrant::Util::FileChecksum.new(path, :sha256).checksum == checksum
        throw :iso_path, Pathname.new(path)
      end
      iso_sentinel.unlink
    end

    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(.to_yaml) }

      env[:env].host.capability(
        :create_iso,
        source_dir,
        volume_id: "cidata"
      ).tap { |path|
        checksum = Vagrant::Util::FileChecksum.new(path.to_path, :sha256).checksum
        iso_sentinel.write("#{checksum}:#{path.to_path}")
      }
    ensure
      FileUtils.remove_entry(source_dir)
    end
  end

  attach_disk_config(machine, env, iso_path.to_path)
end