<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Alex Tomkins - vagrant</title>
    <link>https://www.alextomkins.com/tag/vagrant/</link>
    <atom:link href="https://www.alextomkins.com/tag/vagrant/feed.xml" rel="self" type="application/rss+xml" />
    <description>Posts tagged with vagrant</description>
    <language>en</language>
    
    
    
    
    
    
    
    <item>
      <title>Testing cloud-init with Vagrant</title>
      <link>https://www.alextomkins.com/2016/09/testing-cloud-init-with-vagrant/</link>
      <guid>https://www.alextomkins.com/2016/09/testing-cloud-init-with-vagrant/</guid>
      <pubDate>Sun, 18 Sep 2016 21:22:00 +0000</pubDate>
      <description><![CDATA[<p><a href="https://cloudinit.readthedocs.io/">cloud-init</a> is a standard tool in most cloud computing
environments as it's an easy way to provide configuration for provisioning server instances. You
provide a YAML config file and it'll use the data to configure the server as needed.</p>
<p>Testing cloud-init in a public can be a bit problematic and time consuming, having to relaunch
servers to test the config from a pristine state. It's possible to reset the cloud-init config on
the server - but if you want to test that your config file works as expected, it's best to test
from a freshly booted server.</p>
<p>Thanks to the <a href="https://atlas.hashicorp.com/ubuntu">Ubuntu Vagrant boxes</a> which are built with
cloud-init support, we can test our cloud-init configuration with
<a href="https://www.vagrantup.com/">Vagrant</a> easily. We'll be using the Ubuntu 16.04 Xenial Xerus box to
test with - as it's the latest Ubuntu LTS release.</p>
<h1>Creating a cloud-init ISO</h1>
<p>cloud-init can support multiple data sources for configuration, one of them being an CD image.
We'll be using this as an easy way to provide our config files. To generate this we can use
genisoimage/mkisofs.</p>
<p>To install on Debian/Ubuntu:</p>
<pre><code>$ sudo apt-get install genisoimage</code></pre>
<p>To install on OS X Macports:</p>
<pre><code>$ sudo port install cdrtools</code></pre>
<p>Next up we'll need our <code>user-data</code> file, the main cloud-init config file:</p>
<pre><code>#cloud-config
chpasswd:
  list: |
    ubuntu:RANDOM
  expire: False

manage_etc_hosts: localhost

ssh_authorized_keys:
  - ssh-rsa
    AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ==
    vagrant insecure public key</code></pre>
<p>This can be customised with the configuration you need to test, however in this example we're
adding the default Vagrant insecure public SSH key. Don't worry - Vagrant will replace it shortly
after rebooting.</p>
<p>Now we need a <code>meta-data</code> file:</p>
<pre><code>instance-id: iid-0123456789abcdef
local-hostname: ubuntu-xenial</code></pre>
<p>This is just the bare minimum - cloud providers usually provide more data. If you depend on other
data provided as metadata then just add it as needed.</p>
<p>To create the ISO image we'll create a <code>Makefile</code>:</p>
<pre><code>nocloud.iso: meta-data user-data
    mkisofs \
        -joliet -rock \
        -volid "cidata" \
        -output nocloud.iso meta-data user-data</code></pre>
<p>Don't forget - a Makefile must be indented with tabs and not spaces!</p>
<p>Now to create the ISO file, all we have to do is run:</p>
<pre><code>$ make</code></pre>
<p>And you'll have a brand new <code>nocloud.iso</code> ISO image.</p>
<h1>cloud-init Vagrantfile</h1>
<p>Now we can create a Vagrantfile to test our newly generated ISO image:</p>
<pre><code>$ vagrant init ubuntu/xenial64</code></pre>
<p>This will create a sample Vagrantfile, which we'll need to edit:</p>
<pre><code># -*- mode: ruby -*-
# vi: set ft=ruby :

CLOUD_CONFIG_PATH = File.join(File.dirname(__FILE__), "nocloud.iso")

Vagrant.require_version ">= 1.8.0"

Vagrant.configure(2) do |config|
    config.vm.box = "ubuntu/xenial64"

    # Disable SSH password for 16.04 - we'll add the insecure Vagrant key
    # (don't worry, it's just an example and gets replaced anyway)
    config.ssh.password = nil

    # Disable shared folders
    config.vm.synced_folder ".", "/vagrant", disabled: true

    # Tweak virtualbox
    config.vm.provider :virtualbox do |vb|
        # Attach nocloud.iso to the virtual machine
        vb.customize [
            "storageattach", :id,
            "--storagectl", "SCSI",
            "--port", "1",
            "--type", "dvddrive",
            "--medium", CLOUD_CONFIG_PATH
        ]

        # Speed up machine startup by using linked clones
        vb.linked_clone = true
    end
end</code></pre>
<p>This creates a new virtual machine based on the xenial64 Vagrant box, but replaces the CD/DVD port
with our <code>nocloud.iso</code> file.</p>
<p>All you have to do at this point is:</p>
<pre><code>$ vagrant up</code></pre>
<p>Vagrant will start up a virtual machine, customised with your cloud-init config file. If anything
goes wrong you can easily <code>vagrant destroy</code> the machine and try again - without the cost of
starting a new server in a public cloud.</p>
<h1>Using your own SSH keys</h1>
<p>If you're developing a user-data file and want to use your own SSH keys instead of Vagrant
replacing it on every boot, edit the Vagrantfile and add these lines:</p>
<pre><code>config.ssh.private_key_path = File.expand_path("~/.ssh/id_rsa")
config.ssh.insert_key = false</code></pre>
<p>Don't forget to edit the user-data file and add your own public key to <code>ssh_authorized_keys</code>, and
run <code>make</code> to generate a new ISO image.</p>
<h1>Conclusion</h1>
<p>Testing cloud-init with Vagrant is quick and easy once you've got everything setup.</p>
<p>Source code for this is available in the
<a href="https://github.com/tomkins/cloud-init-vagrant">cloud-init-vagrant</a> repo on GitHub.</p>
]]></description>
    </item>
    
    
    
    <item>
      <title>Automating Ansible groups with Vagrant</title>
      <link>https://www.alextomkins.com/2016/09/automating-ansible-groups-with-vagrant/</link>
      <guid>https://www.alextomkins.com/2016/09/automating-ansible-groups-with-vagrant/</guid>
      <pubDate>Sat, 10 Sep 2016 21:57:00 +0000</pubDate>
      <description><![CDATA[<p><a href="https://www.vagrantup.com/">Vagrant</a> is a great tool for creating development environments and
testing deployment scripts, especially with <a href="https://www.ansible.com/">Ansible</a>. Testing an Ansible
playbook is can be as simple as <code>vagrant up</code>.</p>
<h1>The problem</h1>
<p>Ansible uses an inventory file to define the SSH details for each server, what groups a server
belongs to - which then allows you to run certain roles/playbooks on specific servers. However the
two options with Vagrant in dealing with the inventory file are rather limited.</p>
<p>The first option is the auto generated inventory, Vagrant will create a fresh inventory file based
on the servers listed in the Vagrantfile which are currently up and running. This inventory file
gets populated with the correct SSH details, however it means it starts with an empty inventory
file which is only populated by setting <code>ansible.groups</code>.</p>
<p>The second option is the static inventory, which Vagrant will use instead of creating a dynamic
inventory file. The downside with this would be duplicating the existing hosts file just to edit
the SSH details, as well as hardcoding the IP addresses of all your Vagrant virtual machines.</p>
<h1>The solution</h1>
<p>We can use the <a href="https://github.com/aisrael/ansibler">ansibler</a> Ruby gem to read the hosts file to
automatically populate <code>ansible.groups</code>.</p>
<p>Installation is easy, as Vagrant maintains plugins separately from other Ruby libraries:</p>
<pre><code>$ vagrant plugin install ansibler
Installing the 'ansibler' plugin. This can take a few minutes...
Installed the plugin 'ansibler (0.2.2)'!
</code></pre>
<p>Now we need to update the Vagrantfile. First of all we're going to read the existing hosts file,
and populate the <code>ANSIBLE_GROUPS</code> variable with a hash of all the groups:</p>
<pre><code>inventory = Ansible::Inventory.read_file('hosts')

ANSIBLE_GROUPS = {}

inventory.groups.each do |group|
    ANSIBLE_GROUPS["#{group.name}"] = group.hosts.map{ |host| host.name }
end</code></pre>
<p>Next up is updating the Ansible provisioner config:</p>
<pre><code>config.vm.provision "ansible" do |ansible|
    # ...
    ansible.groups = ANSIBLE_GROUPS
end</code></pre>
<p>And that's it!</p>
<p>Now that <code>ansible.groups</code> contains an exact copy of all the group config from your hosts file,
every time Vagrant creates a new dynamic inventory file it'll be populated with all the group
configuration from your main hosts file.</p>
]]></description>
    </item>
    
    
    
    
    
    
    
    
    
    
    
    
    
  </channel>
</rss>
