Skip to content

Instantly share code, notes, and snippets.

@trombik
Last active November 6, 2016 11:17
Show Gist options
  • Save trombik/d01e280f02c78618429e334d8e4995c0 to your computer and use it in GitHub Desktop.
Save trombik/d01e280f02c78618429e334d8e4995c0 to your computer and use it in GitHub Desktop.
my init script to create an ansible role
#!/bin/sh
set -e
usage() {
cat <<'__EOF__'
ansible-role-init [ directory ]
initialize ansible role git repo
__EOF__
}
args=`getopt h $*`
if [ $? -ne 0 ]; then
usage
exit 2
fi
set -- $args
while true; do
case "$1" in
-h)
usage
exit 2
;;
--)
shift; break
;;
esac
done
DEST=$1
if [ -z "${DEST}" ]; then
echo "requires role dir name"
usage
exit 2
fi
if [ -d "${DEST}" ]; then
echo "target directory ${DEST} exists"
exit 1
fi
ROLENAME=`echo $DEST | sed -e 's/^ansible-role-//' -e 's/-/_/g'`
if [ -z ${ROLENAME} ]; then
echo "invalid role name: ${DEST}"
echo "valid role name is: ansible-role-\$ROLENAME"
echo "\$ROLENAME should be [0-9a-zA-Z-_]+"
exit 1
fi
BOXNAME_WITHOUT_USERNAME="freebsd-10.3-amd64"
BOXNAME="trombik\/ansible-${BOXNAME_WITHOUT_USERNAME}"
YYYY=`date +%Y`
MYID=`whoami`
PLATFORM=`uname -s`
if [ "${PLATFORM}" = 'Darwin' ]; then
MYNAME=`finger ${MYID} | awk -F: '{ print $3 }' | head -n1 | sed 's/^ //'`
else
MYNAME=`getent passwd ${MYID} | cut -d ':' -f 5 | cut -d ',' -f 1`
fi
mkdir "${DEST}"
cd "${DEST}"
required_directories="defaults files handlers tasks templates vars extra_modules extra_roles filter_plugins library meta tests"
for i in ${required_directories}; do
mkdir "${i}"
touch "${i}/.keepme"
done
required_sub_directories="tests/integration tests/serverspec tests/travisci tests/integration/example/group_vars tests/integration/example/host_vars tests/integration/example/inventories tests/integration/example/roles tests/integration/example/spec"
for i in ${required_sub_directories}; do
mkdir -p "${i}"
touch "${i}/.keepme"
done
touch tests/integration/example/inventories/staging
cat > Rakefile <<'__EOF__'
require 'pathname'
root_dir = Pathname.new(__FILE__).dirname
integration_test_dir = root_dir + 'tests' + 'integration'
integration_test_dirs = Pathname.new(integration_test_dir).children.select { |c| c.directory? }
task default: %w[ test ]
desc 'run all tests'
task :test do
integration_test_dirs.each do |d|
rakefile = d + 'Rakefile'
if rakefile.exist? and rakefile.file?
Dir.chdir(d) do
puts "entering to %s" % [ d ]
begin
puts 'running rake'
sh 'rake'
ensure
sh 'rake clean'
end
end
else
puts 'Rakefile does not exist, skipping'
end
end
end
task :clean do
integration_test_dirs.each do |d|
rakefile = d + 'Rakefile'
if rakefile.exist? and rakefile.file?
Dir.chdir(d) do
puts "entering to %s" % [ d ]
begin
puts 'running rake clean'
sh 'rake clean'
rescue Exception => e
puts 'rake clean clean failed:'
puts e.message
puts e.backtrace.inspect
end
end
end
end
end
__EOF__
cat > .kitchen.yml <<'__EOF__'
---
driver:
name: vagrant
transport:
name: rsync
provisioner:
hosts: test-kitchen
name: ansible_playbook
require_chef_for_busser: false
require_ruby_for_busser: false
ansible_verbosity: 1
ansible_verbose: true
ansible_extra_flags: <%= ENV['ANSIBLE_EXTRA_FLAGS'] %>
http_proxy: <%= ENV['ANSIBLE_PROXY'] %>
idempotency_test: true
additional_copy_path:
- extra_modules
- filter_plugins
- extra_roles
ansible_vault_password_file: <%= File.expand_path(ENV['ANSIBLE_VAULT_PASSWORD_FILE'] || '') %>
platforms:
- name: BOXNAME_WITHOUT_USERNAME
driver:
box: BOXNAME
box_check_update: false
driver_config:
ssh:
shell: '/bin/sh'
provisioner:
extra_vars:
ansible_python_interpreter: '/usr/local/bin/python'
suites:
- name: default
provisioner:
name: ansible_playbook
playbook: tests/serverspec/default.yml
verifier:
name: shell
command: rspec -c -f d -I tests/serverspec tests/serverspec/default_spec.rb
__EOF__
# https://gist.github.com/fnichol/7551540
cat > .kitchen.local.yml <<'__EOF__'
<%
require 'socket'
# @return [String] public IP address of workstation used for egress traffic
def local_ip
@local_ip ||= begin
# turn off reverse DNS resolution temporarily
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
# open UDP socket so that it never send anything over the network
UDPSocket.open do |s|
s.connect '8.8.8.8', 1 # any global IP address works here
s.addr.last
end
ensure
Socket.do_not_reverse_lookup = orig
end
end
# @return [Integer] default listening port
def local_port ; 8080 ; end
# @return [String] the proxy URL
def http_proxy_url ; "http://#{local_ip}:#{local_port}" ; end
# @return [TrueClass,FalseClass] whether or not the port is listening
def proxy_running?
socket = TCPSocket.new(local_ip, local_port)
true
rescue SocketError, Errno::ECONNREFUSED,
Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
false
rescue Errno::EPERM, Errno::ETIMEDOUT
false
ensure
socket && socket.close
end
%>
---
<% if proxy_running? %>
provisioner:
http_proxy: <%= http_proxy_url %>
https_proxy: <%= http_proxy_url %>
<% end %>
__EOF__
cat > defaults/main.yml <<'__EOF__'
CHANGEME_user: CHANGEME
CHANGEME_group: CHANGEME
CHANGEME_log_dir: /var/log/CHANGEME
CHANGEME_db_dir: "{{ __CHANGEME_db_dir }}"
CHANGEME_service: CHANGEME
CHANGEME_conf_dir: "{{ __CHANGEME_conf_dir }}"
CHANGEME_conf_file: "{{ __CHANGEME_conf_dir }}/CHANGEME.conf"
CHANGEME_flags: ""
__EOF__
cat > vars/FreeBSD.yml <<'__EOF__'
__CHANGEME_db_dir: /var/db/CHANGEME
__CHANGEME_conf_dir: /usr/local/etc/CHANGEME
__EOF__
cat > tasks/main.yml <<'__EOF__'
---
# tasks file for ansible-role-CHANGEME
- include_vars: "{{ ansible_os_family }}.yml"
- include: "install-{{ ansible_os_family }}.yml"
- name: Create log directory
file:
path: "{{ CHANGEME_log_dir }}"
mode: 0755
owner: "{{ CHANGEME_user }}"
group: "{{ CHANGEME_group }}"
state: directory
notify: Restart CHANGEME
- name: Create db directory
file:
path: "{{ CHANGEME_db_dir }}"
mode: 0755
owner: "{{ CHANGEME_user }}"
group: "{{ CHANGEME_group }}"
state: directory
notify: Restart CHANGEME
- name: Create CHANGEME.conf
template:
src: CHANGEME.conf.j2
dest: "{{ CHANGEME_conf_file }}"
notify: Restart CHANGEME
- name: Start CHANGEME
service:
name: "{{ CHANGEME_service }}"
enabled: true
state: started
__EOF__
cat > tests/serverspec/default.yml <<'__EOF__'
- hosts: localhost
roles:
- ansible-role-CHANGEME
vars:
CHANGEME_config: ""
__EOF__
cat > handlers/main.yml <<'__EOF__'
---
- name: Restart CHANGEME
service:
name: CHANGEME
state: restarted
__EOF__
cat > tasks/install-FreeBSD.yml <<'__EOF__'
---
- name: Install CHANGEME
pkgng:
name: CHANGEME
state: present
__EOF__
cat > .travis.yml <<'__EOF__'
---
language: python
python: "2.7"
# Use the new container infrastructure
sudo: false
# Install ansible
addons:
apt:
packages:
- python-pip
- wget
- curl
install:
# Install ansible
- pip install ansible
# Check ansible version
- ansible --version
# Create ansible.cfg with correct roles_path
- printf '[defaults]\nroles_path=../:extra_roles' >ansible.cfg
script:
# See if the repo is private
- if curl --silent --output /dev/null --dump-header - "https://github.com/${TRAVIS_REPO_SLUG}" | grep "Status:[[:space:]]*404"; then touch .private_repo; fi
# Basic role syntax check
#
# If it is a private repo, it _usually_ has secret information, or encrypted
# file. As the information security policy does not allow decryption key to
# be transfered to third-party, encrypted files cannot be decrypted in travis
# CI environment. Skip syntax check when it is a private repo.
#
- "if [ ! -f .private_repo ]; then ansible-playbook tests/travisci/tests.yml -i tests/travisci/inventory --syntax-check; fi"
# download the QA scripts
- mkdir bin
- wget -O bin/ansible-role-qa https://gist.githubusercontent.com/trombik/047364853d2f8070da9d4f360b4b9da9/raw
- wget -O bin/ansible-role-init https://gist.githubusercontent.com/trombik/d01e280f02c78618429e334d8e4995c0/raw
- chmod +x bin/*
# use recent ruby, instead of default ruby 1.9
- rvm install ruby-2.1.10
- rvm use 2.1.10
# git complains if user and email are not set
- git config --global user.name "Your Name"
- git config --global user.email "you@example.com"
# surpress a warning from `ansible-role-qa`
- touch .kitchen.local.yml
# run the QA test
- export PATH="${PATH}:${PWD}/bin"
- ansible-role-qa
notifications:
webhooks: https://galaxy.ansible.com/api/v1/notifications/
__EOF__
cat > tests/travisci/tests.yml <<'__EOF__'
---
- hosts: localhost
remote_user: root
roles:
- ansible-role-CHANGEME
__EOF__
cat > tests/travisci/inventory <<'__EOF__'
localhost
__EOF__
cat > tests/serverspec/spec_helper.rb <<'__EOF__'
require 'serverspec'
set :backend, :ssh
options = Net::SSH::Config.for(host)
options[:host_name] = ENV['KITCHEN_HOSTNAME']
options[:user] = ENV['KITCHEN_USERNAME']
options[:port] = ENV['KITCHEN_PORT']
options[:keys] = ENV['KITCHEN_SSH_KEY']
set :host, options[:host_name]
set :ssh_options, options
set :env, :LANG => 'C', :LC_ALL => 'C'
__EOF__
cat > tests/serverspec/default_spec.rb <<'__EOF__'
require 'spec_helper'
require 'serverspec'
package = 'CHANGEME'
service = 'CHANGEME'
config = '/etc/CHANGEME/CHANGEME.conf'
user = 'CHANGEME'
group = 'CHANGEME'
ports = [ PORTS ]
log_dir = '/var/log/CHANGEME'
db_dir = '/var/lib/CHANGEME'
case os[:family]
when 'freebsd'
config = '/usr/local/etc/CHANGEME.conf'
db_dir = '/var/db/CHANGEME'
end
describe package(package) do
it { should be_installed }
end
describe file(config) do
it { should be_file }
its(:content) { should match Regexp.escape('CHANGEME') }
end
describe file(log_dir) do
it { should exist }
it { should be_mode 755 }
it { should be_owned_by user }
it { should be_grouped_into group }
end
describe file(db_dir) do
it { should exist }
it { should be_mode 755 }
it { should be_owned_by user }
it { should be_grouped_into group }
end
case os[:family]
when 'freebsd'
describe file('/etc/rc.conf.d/CHANGEME') do
it { should be_file }
end
end
describe service(service) do
it { should be_running }
it { should be_enabled }
end
ports.each do |p|
describe port(p) do
it { should be_listening }
end
end
__EOF__
cat > .gitignore <<'__EOF__'
/.kitchen/
.kitchen.local.yml
*.swp
.bundle/
vendor/
Gemfile.lock
__EOF__
cat > .ackrc <<'__EOF__'
--ignore-dir=vendor
--ignore-dir=.kitchen
__EOF__
cat > meta/main.yml <<'__EOF__'
---
galaxy_info:
author: MYNAME
description: Configures something
company: Reallyenglish
license: BSD
min_ansible_version: 2.0
platforms:
- name: FreeBSD
versions:
- 10.3
# - name: OpenBSD
# versions:
# - 6.0
# - name: Ubuntu
# versions:
# - trusty
# - name: EL
# versions:
# - 7
galaxy_tags: []
#dependencies:
# - { role: username.common, some_parameter: 3 }
__EOF__
cat > CHANGELOG.md <<'__EOF__'
## Release 0.0.0
* Initail release
__EOF__
cat > README.md <<'__EOF__'
# ansible-role-CHANGEME
A brief description of the role goes here.
# Requirements
None
# Role Variables
| variable | description | default |
|----------|-------------|---------|
# Dependencies
None
# Example Playbook
```yaml
```
# License
```
Copyright (c) YYYY MYNAME <MYID@reallyenglish.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
```
# Author Information
MYNAME <MYID@reallyenglish.com>
This README was created by [ansible-role-init](https://gist.github.com/trombik/d01e280f02c78618429e334d8e4995c0)
__EOF__
cat > LICENSE <<'__EOF__'
Copyright (c) YYYY MYNAME <MYID@reallyenglish.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
__EOF__
cat > Jenkinsfile <<'__EOF__'
node ('virtualbox') {
def directory = "DESTNAME"
env.ANSIBLE_VAULT_PASSWORD_FILE = "~/.ansible_vault_key"
stage 'Clean up'
deleteDir()
stage 'Checkout'
sh "mkdir $directory"
dir("$directory") {
try {
checkout scm
sh "git submodule update --init"
} catch (e) {
currentBuild.result = 'FAILURE'
notifyBuild(currentBuild.result)
throw e
}
}
dir("$directory") {
stage 'bundle'
try {
sh "bundle install --path ${env.JENKINS_HOME}/vendor/bundle"
} catch (e) {
currentBuild.result = 'FAILURE'
notifyBuild(currentBuild.result)
throw e
}
stage 'bundle exec kitchen test'
try {
sh 'bundle exec kitchen test'
} catch (e) {
currentBuild.result = 'FAILURE'
notifyBuild(currentBuild.result)
throw e
} finally {
sh 'bundle exec kitchen destroy'
}
/* if you have integration tests, uncomment the stage below
stage 'integration'
try {
// use native rake instead of bundle exec rake
// https://github.com/docker-library/ruby/issues/73
sh 'rake test'
} catch (e) {
currentBuild.result = 'FAILURE'
notifyBuild(currentBuild.result)
throw e
} finally {
sh 'rake clean'
}
*/
stage 'Notify'
notifyBuild(currentBuild.result)
step([$class: 'GitHubCommitNotifier', resultOnFailure: 'FAILURE'])
}
}
def notifyBuild(String buildStatus = 'STARTED') {
// build status of null means successful
buildStatus = buildStatus ?: 'SUCCESSFUL'
// Default values
def colorName = 'RED'
def colorCode = '#FF0000'
def subject = "${buildStatus}: Job '${env.JOB_NAME} build #${env.BUILD_NUMBER}'"
def summary = "${subject} <a href='${env.BUILD_URL}'>${env.BUILD_URL}</a>"
def details = """<p>STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':</p>
<p>Check console output at &QUOT;<a href='${env.BUILD_URL}'>${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>&QUOT;</p>"""
// Override default values based on build status
if (buildStatus == 'STARTED') {
color = 'YELLOW'
colorCode = '#FFFF00'
} else if (buildStatus == 'SUCCESSFUL') {
color = 'GREEN'
colorCode = '#00FF00'
} else {
color = 'RED'
colorCode = '#FF0000'
}
hipchatSend (color: color, notify: true, message: summary)
}
/* vim: ft=groovy */
__EOF__
cat > Gemfile <<'__EOF__'
source 'https://rubygems.org'
# rack 2.x requires ruby >= 2.2.2
gem 'rack', '~> 1.6.4'
gem 'rake', '~> 11.1.2'
gem 'rspec', '~> 3.4.0'
gem "test-kitchen", '~> 1.6.0'
gem "kitchen-vagrant", '~> 0.19.0'
# use patched kitchen-ansible
gem "kitchen-ansible", '~> 0.40.1', :git => 'https://github.com/trombik/kitchen-ansible.git', :branch => 'freebsd_support'
gem "kitchen-sync", '~> 2.1.1', :git => 'https://github.com/trombik/kitchen-sync.git', :branch => 'without_full_path_to_rsync'
gem 'kitchen-verifier-shell', '~> 0.2.0'
gem 'kitchen-verifier-serverspec', '~> 0.3.0'
gem 'infrataster', '~> 0.3.2', :git => 'https://github.com/trombik/infrataster.git', :branch => 'reallyenglish'
gem 'serverspec', '~> 2.37.2'
gem 'specinfra', '>= 2.63.2' # OpenBSD's `port` is fixed in this version
__EOF__
cat > tests/integration/example/site.yml <<'__EOF__'
- include: server.yml
- include: client.yml
__EOF__
cat > tests/integration/example/.gitignore <<'__EOF__'
*.retry
roles/*/
.vagrant/
__EOF__
cat > tests/integration/example/.rspec <<'__EOF__'
--format documentation
__EOF__
cat > tests/integration/example/Rakefile <<'__EOF__'
require 'tempfile'
require 'pathname'
require 'net/http'
require 'tempfile'
test_dir = Pathname.new(__FILE__).dirname
role_dir = Pathname.new(test_dir).parent.parent.parent
role_name = Pathname.new(role_dir).basename
required_roles = %w[
]
def fetch(uri_str, limit = 10)
raise ArgumentError, 'too many HTTP redirects' if limit == 0
response = Net::HTTP.get_response(URI(uri_str))
case response
when Net::HTTPSuccess then
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
raise "HTTP response is not success nor redirect (value: #{response.value})"
end
end
task default: %w[ test ]
desc 'run rspec'
task :test => [ :clean, :prepare, :do_test, :ensure_clean ] do
end
desc 'clean'
task :clean => [ :clean_vagrant, :clean_roles, :clean_role ] do
end
desc 'destroy vagrant nodes'
task :clean_vagrant do
sh 'vagrant destroy -f'
end
desc 'rm roles/*'
task :clean_role do
sh 'rm -rf roles/*'
sh 'rm -f *.retry'
end
desc 'prepare the test environment'
task :prepare => [ :prepare_role, :prepare_roles, :vagrant_up ] do
end
desc 'import required roles'
task :prepare_roles do
required_roles.each do |r|
Dir.chdir('roles') do
res = fetch "https://github.com/reallyenglish/#{r}/archive/master.tar.gz"
file = Tempfile.new('tar')
file.write(res.body)
file.close
sh "tar xf #{file.path}"
file.unlink
sh "mv #{r}-master #{r}"
end
end
end
desc 'clean up required roles'
task :clean_roles do
required_roles.each do |r|
Dir.chdir('roles') do
sh "rm -rf #{r}"
end
end
end
desc 'prepare role'
task :prepare_role do
ignore_files = %w[ vendor .kitchen .git tests spec ].map { |f| "#{role_name}/#{f}" }
tmpfile = Tempfile.new('.tarignore')
tmpfile.write ignore_files.join("\n")
tmpfile.close
sh "tar -c -X #{tmpfile.path} -C #{role_dir.parent} -f - #{role_name} | tar -x -C #{test_dir}/roles -f -"
end
desc 'vagrant up'
task :vagrant_up do
sh 'vagrant up'
end
desc 'do clean task even if it has been executed'
task :ensure_clean do
Rake::Task["clean"].all_prerequisite_tasks.each do |task|
task.reenable
end
Rake::Task["clean"].reenable
Rake::Task["clean"].invoke
end
desc 'Do the test'
task :do_test do
sh 'bundle exec rspec'
end
__EOF__
cat > tests/integration/example/spec/spec_helper.rb <<'__EOF__'
require 'infrataster/rspec'
require 'capybara'
ENV['VAGRANT_CWD'] = File.dirname(__FILE__)
ENV['LANG'] = 'C'
if ENV['JENKINS_HOME']
# XXX "bundle exec vagrant" fails to load.
# https://github.com/bundler/bundler/issues/4602
#
# > bundle exec vagrant --version
# bundler: failed to load command: vagrant (/usr/local/bin/vagrant)
# Gem::Exception: can't find executable vagrant
# /usr/local/lib/ruby/gems/2.2/gems/bundler-1.12.1/lib/bundler/rubygems_integration.rb:373:in `block in replace_bin_path'
# /usr/local/lib/ruby/gems/2.2/gems/bundler-1.12.1/lib/bundler/rubygems_integration.rb:387:in `block in replace_bin_path'
# /usr/local/bin/vagrant:23:in `<top (required)>'
#
# this causes "vagrant ssh-config" to fail, invoked in a spec file, i.e. when
# you need to ssh to a vagrant host.
#
# include the path of bin to vagrant
vagrant_real_path = `pkg info -l vagrant | grep -v '/usr/local/bin/vagrant' | grep -E 'bin\/vagrant$'| sed -e 's/^[[:space:]]*//'`
vagrant_bin_dir = File.dirname(vagrant_real_path)
ENV['PATH'] = "#{vagrant_bin_dir}:#{ENV['PATH']}"
end
Infrataster::Server.define(
:client1,
'192.168.21.100',
vagrant: true
)
Infrataster::Server.define(
:server1,
'192.168.21.200',
vagrant: true
)
def fetch(uri_str, limit = 10)
raise ArgumentError, 'too many HTTP redirects' if limit == 0
response = Net::HTTP.get_response(URI(uri_str))
case response
when Net::HTTPSuccess then
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
raise "HTTP response is not success nor redirect (value: #{response.value})"
end
end
def retry_and_sleep(options = {}, &block)
opts = {
:tries => 60,
:sec => 10,
:on => [ Exception ],
:verbose => false
}.merge(options)
tries, sec, on, verbose = opts[:tries], opts[:sec], opts[:on], opts[:verbose]
i = 1
begin
yield
rescue *on => e
warn "rescue an excpetion %s" % [ e.class ] if verbose
warn e.message if verbose
if (tries -= 1) > 0
warn "retrying (remaining: %d)" % [ tries ]
warn "sleeping %d sec" % [ sec ] if verbose
sleep sec
i += 1
retry
end
end
end
require 'shellwords'
class Vagrant
def initialize
@status # Process::Status
@out
@err
end
def up(server)
execute('up', '', server)
end
def boot(server)
execute('up', '--no-provision', server)
end
def provision(server)
execute('provision', '', server)
end
def destroy(server)
execute('destroy', '--force', server)
end
def execute(command, opt, server)
begin
command = "vagrant #{Shellwords.escape(command)}" + ' '
command += "#{Shellwords.escape(opt)}" + ' ' if not opt.empty?
command += "#{Shellwords.escape(server)}"
@out, @err, @status = Open3.capture3(command)
rescue SystemCallError => e
@status = e
end
status
end
def status
@status
end
def stdout
@out
end
def stdout_lines
@out.split("\n").to_a
end
def stderr
@err
end
def stderr_lines
@err.split("\n").to_a
end
def status
@status
end
def success?
return false if @success.nil?
return false if @status.is_a?(Exception)
@status.success?
end
end
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
__EOF__
cat > tests/integration/example/spec/default_spec.rb <<'__EOF__'
require 'spec_helper'
class ServiceNotReady < StandardError
end
sleep 10 if ENV['JENKINS_HOME']
context 'after provisioning finished' do
describe server(:client1) do
it 'should be able to ping server' do
result = current_server.ssh_exec("ping -c 1 #{ server(:server1).server.address } && echo OK")
expect(result).to match(/OK/)
end
end
describe server(:server1) do
it 'should be able to ping client' do
result = current_server.ssh_exec("ping -c 1 #{ server(:client1).server.address } && echo OK")
expect(result).to match(/OK/)
end
end
end
__EOF__
cat > tests/integration/example/Vagrantfile <<'__EOF__'
require 'socket'
# @return [String] public IP address of workstation used for egress traffic
def local_ip
@local_ip ||= begin
# turn off reverse DNS resolution temporarily
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
# open UDP socket so that it never send anything over the network
UDPSocket.open do |s|
s.connect '8.8.8.8', 1 # any global IP address works here
s.addr.last
end
ensure
Socket.do_not_reverse_lookup = orig
end
end
# @return [Integer] default listening port
def local_port ; 8080 ; end
# @return [String] the proxy URL
def http_proxy_url ; "http://#{local_ip}:#{local_port}" ; end
# @return [TrueClass,FalseClass] whether or not the port is listening
def proxy_running?
socket = TCPSocket.new(local_ip, local_port)
true
rescue SocketError, Errno::ECONNREFUSED,
Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
false
rescue Errno::EPERM, Errno::ETIMEDOUT
false
ensure
socket && socket.close
end
http_proxy = proxy_running? ? http_proxy_url : ''
# Vagrantfile
Vagrant.configure("2") do |config|
config.ssh.shell = "/bin/sh"
config.vm.provider "virtualbox" do |v|
v.memory = 256
v.cpus = 1
end
config.vm.box = "trombik/ansible-freebsd-10.3-amd64"
config.vm.define "client1" do |c|
c.vm.network "private_network", ip: "192.168.21.100"
c.vm.hostname = "client1.virtualbox.reallyenglish.com"
c.vm.provision :ansible do |ansible|
ansible.limit = '192.168.21.100'
ansible.playbook = "site.yml"
ansible.extra_vars = {
ansible_python_interpreter: '/usr/local/bin/python',
http_proxy: http_proxy,
https_proxy: http_proxy,
no_proxy: 'localhost,127.0.0.1,.example.com'
}
ansible.inventory_path = 'inventories/staging'
ansible.verbose = 'v'
end
end
config.vm.define "server1" do |c|
c.vm.network "private_network", ip: "192.168.21.200"
c.vm.hostname = "server1.virtualbox.reallyenglish.com"
c.vm.provision :ansible do |ansible|
ansible.limit = '192.168.21.200'
ansible.playbook = "site.yml"
ansible.extra_vars = {
ansible_python_interpreter: '/usr/local/bin/python',
http_proxy: http_proxy,
https_proxy: http_proxy,
no_proxy: 'localhost,127.0.0.1,.example.com'
}
ansible.inventory_path = 'inventories/staging'
ansible.verbose = 'v'
end
end
end
# vim: ft=ruby
__EOF__
cat > tests/integration/example/server.yml <<'__EOF__'
---
- hosts: server
become: yes
become_method: sudo
environment:
http_proxy: "{{ http_proxy | default() }}"
https_proxy: "{{ https_proxy | default() }}"
no_proxy: "{{ no_proxy | default() }}"
pre_tasks:
roles:
- CHANGEME
__EOF__
cat > tests/integration/example/client <<'__EOF__'
---
- hosts: client
become: yes
become_method: sudo
environment:
http_proxy: "{{ http_proxy | default() }}"
https_proxy: "{{ https_proxy | default() }}"
no_proxy: "{{ no_proxy | default() }}"
pre_tasks:
roles:
- CHANGEME
__EOF__
cat > tests/integration/example/inventories/staging <<'__EOF__'
[client]
192.168.21.100
[server]
192.168.21.200
[all:children]
server
client
__EOF__
cat > tests/integration/example/example.yml <<'__EOF__'
---
- hosts: example
become: yes
become_method: sudo
environment:
http_proxy: "{{ http_proxy | default() }}"
https_proxy: "{{ https_proxy | default() }}"
no_proxy: "{{ no_proxy | default() }}"
pre_tasks:
roles:
- CHANGEME
__EOF__
find . -type f -print0 | \
xargs -0 sed -i'' \
-e "s/CHANGEME/${ROLENAME}/g" \
-e "s/YYYY/${YYYY}/g" \
-e "s/DESTNAME/${DEST}/g" \
-e "s/MYNAME/${MYNAME}/g" \
-e "s/MYID/${MYID}/g" \
-e "s/BOXNAME_WITHOUT_USERNAME/${BOXNAME_WITHOUT_USERNAME}/g" \
-e "s/BOXNAME/${BOXNAME}/g"
git init .
git add .
git commit -m 'initial import'
echo "Successfully created ${ROLENAME}."
echo "You need to run bundle install."
@mitsururike
Copy link

could you merge the fork https://gist.github.com/mitsururike/d8952388f2abbc7ae706ffb6a2225833 to fix the failure on downloading vagrant boxes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment