Massively simpler serverspec invocation

Give up on using test-kitchen, busser, and more of its complexity and
simply run serverspec directly, via SSH.
This commit is contained in:
Leo Simons 2014-08-01 14:16:26 +02:00 committed by wilderrodrigues
parent 04ad01a064
commit 8fb1deb33e
13 changed files with 126 additions and 273 deletions

View File

@ -49,5 +49,3 @@ systemvm.iso
iso/*
rspec.xml
vagrant_ssh_config
.kitchen/

View File

@ -1,15 +0,0 @@
---
driver:
name: vagrant
vagrantfile_erb: Vagrantfile.kitchen
provisioner:
name: shell
platforms:
- name: systemvm
suites:
- name: default
run_list:
attributes:

View File

@ -0,0 +1,3 @@
--format documentation
--format RspecJunitFormatter
--out rspec.xml

View File

@ -1,5 +1,6 @@
source 'https://rubygems.org'
gem 'test-kitchen', :git => 'https://github.com/test-kitchen/test-kitchen.git', :branch => 'master'
gem 'kitchen-vagrant'
gem 'vagrant-wrapper'
gem 'rake'
gem 'rspec', '~> 2.99'
gem 'serverspec', '~> 1.11.0'
gem 'rspec_junit_formatter'

View File

@ -0,0 +1,8 @@
require 'rake'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'spec/*/*_spec.rb'
end
task :default => :spec

View File

@ -0,0 +1,25 @@
#!/bin/bash
# In some cases, while booting a virtual machine, an IDE controller
# will be created for it. It seems that the VirtualBox GUI likes doing
# this: when a particular machine has booted at least once with its
# GUI turned on, this will happen pretty consistently.
#
# Having an IDE controller and a SATA controller breaks the assumptions
# in the systemvm scripts about what disks are attached, causing it to
# not find the systemvm.iso.
#
# So, we delete the IDE controller using Vagrant.
#
# Unfortunately, when the IDE controller does not exist, that deletion
# fails, causing vagrant to fail. To work around this, we inject this
# script into the path, causing vagrant to try to continue booting.
/usr/bin/VBoxManage "$@"
exitcode=$?
if [[ "$1" == "storagectl" ]]; then
exit 0
else
exit ${exitcode}
fi

View File

@ -62,7 +62,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider 'virtualbox' do |vb|
# enable or disable headless mode
vb.gui = true
vb.gui = false
vb.customize ['modifyvm', :id, '--memory', '256']
vb.customize ['storagectl', :id, '--name', 'IDE Controller', '--remove']
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', '1', '--type', 'dvddrive',

View File

@ -1,85 +0,0 @@
#-*- mode: ruby -*-
# vi: set ft=ruby :
include RbConfig
VAGRANTFILE_API_VERSION = '2'
unless ENV['VPC_IP']
puts 'Please specify the VPC IP by settings the VPC_IP environment variable'
puts 'Example: export VPC_IP=192.168.56.30'
puts ''
exit 1
end
VPC_NAME='r-' + ENV['VPC_IP'].split('.').last + '-VM'
if ARGV[0] == 'up'
iso_util=''
case CONFIG['host_os']
when /mswin|windows/i
puts 'Windows is not supported'
exit 1
when /linux|arch/i
iso_util='mkisofs -J -o systemvm.iso ./iso'
when /sunos|solaris/i
puts 'Solaris is not supported'
exit 1
when /darwin/i
iso_util='hdiutil makehybrid -iso -joliet -o systemvm.iso ./iso/'
else
puts 'This OS is not supported'
exit 1
end
system 'rm -rf ./systemvm.iso'
system 'mkdir -p iso/'
unless File.exist? '../../../../../../systemvm/dist/cloud-scripts.tgz'
puts 'No cloud-scripts.tgz found. Did you run the maven build?'
exit 1
end
system 'cp ../../../../../../systemvm/dist/cloud-scripts.tgz iso/'
unless File.exist? '../../../../../../systemvm/dist/systemvm.zip'
puts 'No systemvm.zip found. Did you run the maven build?'
exit 1
end
system 'cp ../../../../../../systemvm/dist/systemvm.zip iso/'
unless File.exist? '../../../vagrant.pub'
puts 'No vagrant.pub found!'
exit 1
end
system 'cp ../../../vagrant.pub iso/authorized_keys'
system 'chmod 600 iso/authorized_keys'
system iso_util
end
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = 'cloudstack/systemvm'
config.vm.network 'private_network', ip: ENV['VPC_IP'], auto_config: false
config.vm.synced_folder 'vagrant', '/vagrant', disabled: true
<% config[:synced_folders].each do |source, destination, options| %>
config.vm.synced_folder "<%= source %>", "<%= destination %>", type: 'rsync', <%= options %>
<% end %>
config.ssh.forward_agent = true
config.ssh.username = 'root'
config.ssh.host = ENV['VPC_IP']
config.ssh.port = 3922
config.ssh.guest_port = 3922
config.vm.provider 'virtualbox' do |vb|
# enable or disable headless mode
vb.gui = true
vb.customize ['modifyvm', :id, '--memory', '256']
vb.customize ['storagectl', :id, '--name', 'IDE Controller', '--remove']
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', '1', '--type', 'dvddrive',
'--medium', './systemvm.iso']
vb.customize('pre-boot', ['modifyvm', :id, '--nic1', 'none'])
extra_data='cmdline:console=hvc0 vpccidr=172.16.0.0/16 domain=devcloud.local dns1=8.8.8.8 dns2=8.8.8.4' +
" template=domP name=#{VPC_NAME} eth0ip=#{ENV['VPC_IP']}" +
' eth0mask=255.255.255.0 type=vpcrouter disable_rp_filter=true'
vb.customize('pre-boot', ['setextradata', :id, 'VBoxInternal/Devices/pcbios/0/Config/DmiOEMVBoxRev', extra_data])
# for internet access
vb.customize ['modifyvm', :id, '--nic8', 'nat']
end
end

View File

@ -1,112 +0,0 @@
#!/bin/bash
set -e
set -x
# script invoked by Test-Kitchen shell provisioner to further
# customize the VM prior to running tests
function setup_networking() {
# for internet access
if [[ ! `grep eth1 /etc/network/interfaces` ]]; then
cat >>/etc/network/interfaces <<END
iface eth1 inet dhcp
auto eth1
END
fi
ifup eth1
}
function install_packages() {
export DEBIAN_FRONTEND=noninteractive
export DEBIAN_PRIORITY=critical
local apt_get="apt-get -q -y --force-yes"
${apt_get} update
if [[ ! `which curl` ]]; then
${apt_get} install curl
fi
if [[ ! `which patch` ]]; then
${apt_get} install patch
fi
if [[ ! `which git` ]]; then
${apt_get} install git
fi
}
function install_chef() {
if [[ ! -f '/opt/chef/embedded/bin/gem' ]]; then
curl -L https://www.opscode.com/chef/install.sh | bash
fi
}
function add_junit_reports_to_serverspec() {
local gem="/opt/chef/embedded/bin/gem"
local gem_install="${gem} install --no-rdoc --no-ri"
${gem_install} rspec rspec_junit_formatter
if [[ ! -d 'serverspec' ]]; then
git clone https://github.com/serverspec/serverspec.git
(cd serverspec; git submodule update --init --recursive)
fi
cd serverspec
git reset --hard
cat >serverspec.patch <<END
diff -u serverspec.gemspec serverspec-spec3.gemspec
--- serverspec.gemspec
+++ serverspec.gemspec
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]
spec.add_runtime_dependency "net-ssh"
- spec.add_runtime_dependency "rspec", "~> 2.99"
+ spec.add_runtime_dependency "rspec", [">= 2.99", '< 4.0']
spec.add_runtime_dependency "rspec-its"
spec.add_runtime_dependency "highline"
spec.add_runtime_dependency "specinfra", "~> 1.22"
END
patch -p0 <serverspec.patch
${gem} build serverspec.gemspec
${gem_install} serverspec-*.gem
cd ..
if [[ ! -d 'busser-serverspec' ]]; then
git clone https://github.com/test-kitchen/busser-serverspec.git
fi
cd busser-serverspec
git reset --hard
cat >busser-serverspec.patch <<END
diff -u lib/busser/serverspec/runner.rb lib/busser/serverspec/runner-spec3.rb
--- lib/busser/serverspec/runner.rb
+++ lib/busser/serverspec/runner.rb
@@ -42,7 +42,7 @@ RSpec::Core::RakeTask.new(:spec) do |t|
end
t.rspec_path = rspec_bin if rspec_bin
- t.rspec_opts = ['--color', '--format documentation']
+ t.rspec_opts = ['--color', '--format documentation', '--format RspecJunitFormatter', '--out /tmp/rspec.xml']
t.ruby_opts = "-I#{base_path}"
t.pattern = "#{base_path}/**/*_spec.rb"
end
END
patch -p0 <busser-serverspec.patch
${gem} build busser-serverspec.gemspec
${gem_install} busser-serverspec-*.gem
cd ..
}
function main() {
setup_networking
install_packages
install_chef
add_junit_reports_to_serverspec
}
# we only run main() if not source-d
return 2>/dev/null || main

View File

@ -0,0 +1,52 @@
require 'serverspec'
require 'pathname'
require 'net/ssh'
require 'pp'
include SpecInfra::Helper::Ssh
include SpecInfra::Helper::DetectOS
#RSpec.configure do |c|
# c.before :all do
# c.path = '/sbin:/usr/sbin'
# end
#end
RSpec.configure do |c|
if ENV['ASK_SUDO_PASSWORD']
require 'highline/import'
c.sudo_password = ask('Enter sudo password: ') { |q| q.echo = false }
else
c.sudo_password = ENV['SUDO_PASSWORD']
end
c.before :all do
block = self.class.metadata[:example_group_block]
if RUBY_VERSION.start_with?('1.8')
file = block.to_s.match(/.*@(.*):[0-9]+>/)[1]
else
file = block.source_location.first
end
host = File.basename(Pathname.new(file).dirname)
if c.host != host
c.ssh.close if c.ssh
c.host = host
options = Net::SSH::Config.for(c.host)
user = options[:user] || Etc.getlogin
config = `vagrant ssh-config default`
if config != ''
config.each_line do |line|
if match = /HostName (.*)/.match(line)
host = match[1]
elsif match = /User (.*)/.match(line)
user = match[1]
elsif match = /IdentityFile (.*)/.match(line)
options[:keys] = [match[1].gsub(/"/,'')]
elsif match = /Port (.*)/.match(line)
options[:port] = match[1]
end
end
end
c.ssh = Net::SSH.start(host, user, options)
end
end
end

View File

@ -0,0 +1,6 @@
require 'spec_helper'
describe file('/etc/cloudstack-release') do
it { should be_file }
its(:content) { should match /Cloudstack Release [0-9]+(\.[0-9]+)+/ }
end

View File

@ -44,16 +44,12 @@ if [[ ! -z "${JENKINS_HOME}" ]]; then
DEBUG=1
fi
kitchen_args=
if [[ "${DEBUG}" == "1" ]]; then
kitchen_args="-l debug"
fi
kitchen="bundle exec kitchen"
# optional (jenkins) build number tag to put into the image filename
VPC_IP="${VPC_IP:-192.168.56.30}"
VPC_IP="${VPC_IP:-192.168.56.254}"
export VPC_IP
# inject our custom VBoxManage wrapper script
export PATH=$PWD:$PATH
###
### Generic helper functions
###
@ -166,39 +162,28 @@ function box_update() {
log INFO "vagrant box update complete"
}
function converge_kitchen() {
log INFO "invoking test-kitchen converge"
${kitchen} create ${kitchen_args}
${kitchen} converge ${kitchen_args}
log INFO "test-kitchen complete"
function vagrant_up() {
log INFO "invoking vagrant up"
vagrant up --no-provision
log INFO "vagrant up complete"
}
function verify_kitchen() {
log INFO "invoking test-kitchen verify"
${kitchen} verify ${kitchen_args}
# re-run busser test with patched serverspec gem to get a rspec.xml
${kitchen} exec ${kitchen_args} -c '
BUSSER_ROOT="/tmp/busser" GEM_HOME="/tmp/busser/gems" GEM_PATH="/tmp/busser/gems" GEM_CACHE="/tmp/busser/gems/cache"
export BUSSER_ROOT GEM_HOME GEM_PATH GEM_CACHE
/tmp/kitchen/bootstrap.sh
sudo -E /tmp/busser/bin/busser test
'
# ssh to machine ourselves to avoid kitchen output
(cd .kitchen/kitchen-vagrant/default-systemvm; vagrant ssh-config) > vagrant_ssh_config
add_on_exit rm -f vagrant_ssh_config
scp -F vagrant_ssh_config default:/tmp/rspec.xml rspec.xml
log INFO "test results in rspec.xml"
log INFO "test-kitchen complete"
function vagrant_provision() {
log INFO "invoking vagrant provision"
vagrant provision
log INFO "vagrant up complete"
}
function destroy_kitchen() {
log INFO "invoking test-kitchen destroy"
${kitchen} destroy ${kitchen_args}
log INFO "test-kitchen destroy complete"
function serverspec() {
log INFO "invoking serverspec"
bundle exec rake spec
log INFO "serverspec complete"
}
function vagrant_destroy() {
log INFO "invoking vagrant destroy"
vagrant destroy -f
log INFO "vagrant destroy complete"
}
###
### Main invocation
@ -207,9 +192,11 @@ function destroy_kitchen() {
function main() {
prepare
box_update
add_on_exit destroy_kitchen
converge_kitchen
verify_kitchen
vagrant_destroy
add_on_exit vagrant_destroy
vagrant_up
vagrant_provision
serverspec
add_on_exit log INFO "BUILD SUCCESSFUL"
}

View File

@ -1,15 +0,0 @@
require 'serverspec'
include Serverspec::Helper::Exec
include Serverspec::Helper::DetectOS
RSpec.configure do |c|
c.before :all do
c.path = '/sbin:/usr/sbin'
end
end
describe file('/etc/cloudstack-release') do
it { should be_file }
its(:content) { should match /Cloudstack Release [0-9]+(\.[0-9]+)+/ }
end