Vagrant / Ansible Multiple Machines

So you’re trying to spin up a stack using Vagrant and provision/configure each of those machines with Ansible but then you get an error something like this:

fatal: [web101] => {'msg': "AnsibleUndefinedVariable: One or more undefined variables: 'dict object' has no attribute 'ansible_default_ipv4'", 'failed': True}
fatal: [web101] => {'msg': "AnsibleUndefinedVariable: One or more undefined variables: 'dict object' has no attribute 'ansible_default_ipv4'", 'failed': True}

It turns out that Ansible hasn’t populated the facts for all your VM’s.

This is because Vagrant runs the Ansible provisioning after each VM! So if you have a multi node VM environment, Ansible will be executed after the very first VM has been created!

The work around hack is to put the provisioning section within the definition of your last VM.

For example:

A Vagrantfile like so:

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

	# Pinch OpsCode centos6.5 box
	config.vm.box = "chef/centos-6.5"
	
	# Two web servers
	(1..2).each do |i|
  		config.vm.define "web#{i}" do |web|
      		web.vm.hostname = "web#{i}"
      		web.vm.network :private_network, ip: "192.168.3.#{i}"
  		end
  	end
  	
  	# Load Balancers
  	config.vm.define "lb1" do |lb1|
		lb1.vm.hostname = "lb1"
		lb1.vm.network :private_network, ip: "192.168.5.1"
  	end
  	
  	# Provisioning with Ansible
  	config.vm.provision "ansible" do |ansible|
		ansible.playbook = "site.yml"
		ansible.sudo = true
	end	
end

Becomes…..

# Vagrantfile to provision the
# stack required for the Ansible lamp-haproxy examples
# https://github.com/ansible/ansible-examples/tree/master/lamp_haproxy

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

	# Pinch OpsCode centos6.5 box
	config.vm.box = "chef/centos-6.5"
	
	# Two web servers
	(101..102).each do |i|
  		config.vm.define "web#{i}" do |web|
      		web.vm.hostname = "web#{i}"
      		web.vm.network :private_network, ip: "172.16.0.#{i}"
  		end
  	end
  	
  	# Load Balancers
  	config.vm.define "lb" do |lb|
		lb.vm.hostname = "lb"
		lb.vm.network :private_network, ip: "172.16.0.104"
		
		# Provisioning only on last machine since ansible deals with multiple hosts
		# See https://github.com/mitchellh/vagrant/issues/1784
  		lb.vm.provision :ansible do |ansible|
    		        ansible.playbook = "provisioning/site.yml"
    		        ansible.inventory_path = "provisioning/inventory"
    		        ansible.sudo = true
			ansible.verbose = "vvv"
  		end
  	end	
end

For more info and specific Vagrant info see:

https://github.com/mitchellh/vagrant/issues/1784

Spring boot data rest gotcha

I’ve been recently playing with Spring Boot and its potential for micro services.

I was particularly interested in the spring-boot-data-rest section. However I struggled to get something working . Any time I tried to make a REST request using cURL, I kept experiencing the following error:

# curl localhost:8080

{
    "timestamp" : 1404054038107,
    "status" : 500,
    "error" : "Internal Server Error",
    "exception" : "java.lang.UnsupportedClassVersionError",
    "message" : "org/atteo/evo/inflector/English : Unsupported major.minor version 51.0",
    "path" : "/"
}
2014-06-29 16:00:38.101 ERROR 7165 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler processing failed; nested exception is java.lang.UnsupportedClassVersionError: org/atteo/evo/inflector/English : Unsupported major.minor version 51.0] with root cause

java.lang.UnsupportedClassVersionError: org/atteo/evo/inflector/English : Unsupported major.minor version 51.0
 at java.lang.ClassLoader.defineClass1(Native Method)
 at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
 at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
 at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
 at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
 at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

After much googling, it turns out that there seems to be a problem with the version of the Inflector library that was being imported by spring-boot.

To fix this I simply updated the dependency used in my pom.xml thus overriding the the spring version. Like so:

<dependency>
    <groupId>org.atteo</groupId>
    <artifactId>evo-inflector</artifactId>
    <version>1.2</version>
</dependency>