Infrastructure as code using Vagrant, Ansible, Cucumber and ServerSpec


Designing and developing VMs as code is at last mainstream. This post is in fact a presentation I give to highlight that we can treat infrastructure code just as we would regular code.

We use TDD/BDD and monitors to spec, implement, test and monitor the resulting VM, keeping its code close to the app’s code and as an integral part of it.

infrastructure as code

From Zero to Deployment: Vagrant, Ansible, Capistrano 3 to deploy your Rails Apps to DigitalOcean automatically (part 0)


tl;dr

Use Cucumber to start us off on our Infrastructure as code journey.

 

Introduction

Part 1 of this blog series demonstrates some Ansible playbooks to create a VM ready for Rails deployment using Vagrant. This is a prequel in the sense that, as a staunch believer in all that’s xDD, I should have started this blog with some Cucumber BDD!
Please forgive my misbehaving and accept my apologies with a few Cucumber scenarios as penance. Hey, it’s never too late to write tests…

The Cucumber Scenarios

As BDD artefacts, they should speak for themselves; write to me if they don’t as it means they were not clear enough!

 

Feature: App deploys to a VM
Background:
Given I have a vm with ip "33.33.33.33"
Scenario: Building the VM
When I provision users on it
Then I can log on to it as the "deploy" user
And I can log on to it as the "root" user
And I can log on to it as the "vagrant" user
Then I remove the VM
Scenario: Adding Linux dependencies
When I provision users on it
When I run the "webserver" ansible playbook
And I log on as "deploy", there is no "ruby"
But "gcc" is present
Then I remove the VM
Scenario: Installing mySQL
When I provision users on it
When I run the "dbserver" ansible playbook
Then I log on as "deploy", then "mysql" is installed
And I can log on as "deploy" to mysql
Then I remove the VM

The Cucumber Steps

Given(/^I have a vm with ip "(.*?)"$/) do |ip|
@ip = ip
output=`vagrant up`
assert $?.success?
end
When(/^I provision users on it$/) do
output=`vagrant provision web`
assert $?.success?
end
Then(/^I can log on to it as the "(.*?)" user$/) do |user|
output=`ssh "#{user}@#{@ip}" exit`
assert $?.success?
end
When(/^I run the "(.*?)" ansible playbook$/) do |playbook|
output=`ansible-playbook devops/"#{playbook}".yml -i devops/webhosts`
assert $?.success?
end
When(/^I log on as "(.*?)", there is no "(.*?)"$/) do |user, program|
@user = user
output = run_remote(user, program)
assert !$?.success?
end
When(/^"(.*?)" is present$/) do |program|
output = run_remote(@user, program)
assert $?.success?
end
Then(/^I log on as "(.*?)", then "(.*?)" is installed$/) do |user, program|
output = run_remote(user, program)
assert $?.success?
end
Then(/^I remove the VM$/) do
output=`vagrant destroy -f`
assert $?.success?
end
Then(/^I can log on as "(.*?)" to mysql$/) do |user|
`ssh "#{user}@#{@ip}" 'echo "show databases;" | mysql -u "#{user}" -praindrop'`
end
def run_remote(user, program)
`ssh "#{user}@#{@ip}" '"#{program}" --version'`
end