WIP knife travis plugin.
require 'chef/knife' | |
module Poise | |
class Travis < Chef::Knife | |
include Chef::Mixin::ShellOut | |
deps do | |
require 'yaml' | |
require 'kitchen' | |
end | |
banner "knife travis" | |
option :ca_path, | |
long: '--ca VALUE', | |
description: 'Path to the TLS CA for Docker.', | |
default: File.expand_path('~/src/poise-docker/tls') # FIX THIS FOR PUBLIC? | |
option :docker, | |
long: '--docker HOSTNAME', | |
description: 'Hostname of the Docker server.', | |
default: 'docker.poise.io' # FIX THIS FOR PUBLIC? | |
option :halite, | |
long: '--halite', | |
description: 'Enable Halite mode', | |
boolean: true, | |
default: false | |
def run | |
if config[:halite] | |
raise 'Not in a gem folder' if Dir['*.gemspec'].empty? | |
else | |
raise 'Not in a cookbook folder' unless File.exists?('metadata.rb') | |
end | |
append_gitignore | |
FileUtils.mkdir_p('test/docker') | |
generate_key | |
encrypt_key | |
write_gemfiles if config[:halite] | |
existing_secure = load_existing_secure | |
write_travis_yml(existing_secure) | |
travis_encrypt(existing_secure) | |
git_misc | |
write_kitchen_travis | |
end | |
private | |
def read_kitchen_yml | |
Kitchen::Loader::YAML.new( | |
project_config: ENV['KITCHEN_YAML'], | |
local_config: ENV['KITCHEN_LOCAL_YAML'], | |
global_config: ENV['KITCHEN_GLOBAL_YAML'], | |
).read | |
end | |
def append_gitignore | |
# Make sure we never commit this stuff accidentally | |
IO.write('.gitignore', "test/docker/\n", mode: 'a') unless !File.exists?('.gitignore') || IO.read('.gitignore').include?('test/docker/') | |
IO.write('.gitignore', ".kitchen.local.yml\n", mode: 'a') unless !File.exists?('.gitignore') || IO.read('.gitignore').include?('.kitchen.local.yml') | |
end | |
def generate_key | |
return if File.exists?('test/docker/docker.crt') | |
ui.info('Generating new TLS key and certificate') | |
shell_out!("openssl req -new -newkey rsa:4096 -keyout test/docker/docker.key -out test/docker/docker.csr -nodes -subj '/CN=Docker client for #{File.basename(Dir.pwd)}/OU=kitchen-docker/O=Coderanger Consulting LLC'") | |
shell_out!("openssl x509 -req -days 3650 -CA #{File.join(config[:ca_path], 'ca.crt')} -CAkey #{File.join(config[:ca_path], 'ca.key')} -CAserial #{File.join(config[:ca_path], 'ca.srl')} -extfile #{File.join(config[:ca_path], 'extfile.cnf')} -in test/docker/docker.csr -out test/docker/docker.crt") | |
File.unlink('test/docker/docker.csr') | |
IO.write('test/docker/docker.ca', IO.read(File.join(config[:ca_path], 'ca.crt'))) | |
end | |
def encrypt_key | |
return if File.exists?('test/docker/docker.pem') | |
ui.info('Creating encrypted TLS key') | |
password = SecureRandom.base64(30) | |
IO.write('test/docker/docker.pass', password) | |
shell_out!("openssl rsa -in test/docker/docker.key -out test/docker/docker-enc.key -aes256 -passout file:test/docker/docker.pass") | |
IO.write('test/docker/docker.pem', IO.read('test/docker/docker.crt')+IO.read('test/docker/docker-enc.key')) | |
File.delete('test/docker/docker-enc.key') | |
end | |
def load_existing_secure | |
return [] unless File.exists?('.travis.yml') | |
secure = [] | |
travis = YAML.load(IO.read('.travis.yml')) | |
if travis['env'] && travis['env']['global'] | |
travis['env']['global'].each do |env_var| | |
if env_var.is_a?(Hash) && env_var.first.first == 'secure' | |
secure << env_var | |
end | |
end | |
end | |
secure | |
end | |
def chef_versions | |
@versions ||= if File.exists?('.kitchen.yml') | |
kitchen = read_kitchen_yml | |
versions = kitchen[:chef_versions] || [] | |
ui.warn("Travis only allows 5 concurrent builds, you have #{versions.length}. This may cause long builds.") if versions.length > 5 | |
versions | |
else | |
[] | |
end | |
end | |
def write_gemfiles | |
FileUtils.mkdir_p('test/gemfiles') | |
chef_versions.each do |ver| | |
gemfile_path = "test/gemfiles/chef-#{ver}.gemfile" | |
IO.write(gemfile_path, "instance_eval(IO.read(File.expand_path('../../../Gemfile', __FILE__)))\ngem 'chef', '~> #{ver}.0'\n") unless File.exists?(gemfile_path) | |
end | |
end | |
def wrap_if(condition, &block) | |
block.call.map do |line| | |
"if [ #{condition} ]; then #{line}; fi" | |
end | |
end | |
def write_travis_yml(existing_secure) | |
travis = { | |
'sudo' => false, | |
'cache' => 'bundler', | |
'language' => 'ruby', | |
'rvm' => %w{2.2}, | |
'addons' => { | |
'apt' => { | |
'packages' => %w{libgecode-dev}, | |
}, | |
}, | |
'env' => { | |
'global' => [ | |
'USE_SYSTEM_GECODE=true', | |
'KITCHEN_LOCAL_YAML=.kitchen.travis.yml', | |
], | |
}, | |
'bundler_args' => '--binstubs=$PWD/bin --jobs 3 --retry 3', | |
'script' => [], | |
} | |
if config[:halite] | |
# Halite mode | |
travis['gemfile'] = chef_versions.map {|v| "test/gemfiles/chef-#{v}.gemfile" } + %w{test/gemfiles/master.gemfile} unless chef_versions.empty? | |
travis['script'] = ['./bin/rake travis'] | |
else | |
travis['env']['matrix'] = chef_versions.map {|v| "CHEF_VERSION=#{v}" } unless chef_versions.empty? | |
travis['script'] = ['./bin/foodcritic -f any .'] | |
end | |
# Do we have test-kitchen? | |
if File.exists?('.kitchen.yml') && !config[:halite] | |
travis['script'] += wrap_if('"$TRAVIS_SECURE_ENV_VARS" = true') do | |
[ | |
'openssl rsa -in test/docker/docker.pem -passin env:KITCHEN_DOCKER_PASS -out test/docker/docker.key', | |
'wget https://get.docker.io/builds/Linux/x86_64/docker-latest -O docker', | |
'chmod +x docker', | |
'./bin/kitchen test -d always', | |
] | |
end | |
end | |
travis['env']['global'] += existing_secure if existing_secure | |
ui.info('Updating .travis.yml') | |
IO.write('.travis.yml', travis.to_yaml) | |
end | |
def travis_encrypt(existing_secure) | |
# Add the password to travis.yml | |
return unless existing_secure.empty? | |
ui.info('Running travis encrypt for key passphrase') | |
cmd = shell_out("travis encrypt KITCHEN_DOCKER_PASS=#{IO.read('test/docker/docker.pass')} -a --skip-version-check") | |
ui.warn('Could not encrypt passphrase for Travis, skipping') if cmd.error? | |
end | |
def git_misc | |
# Git add some bits | |
shell_out!('git add -f test/docker/docker.ca test/docker/docker.pem') | |
end | |
def write_kitchen_travis | |
kitchen = { | |
'driver' => { | |
'name' => 'docker', | |
'binary' => './docker', | |
'socket' => "tcp://#{config[:docker]}:443", | |
'tls_verify' => true, | |
'tls_cacert' => 'test/docker/docker.ca', | |
'tls_cert' => 'test/docker/docker.pem', | |
'tls_key' => 'test/docker/docker.key', | |
} | |
} | |
ui.info('Updating .kitchen.travis.yml') | |
IO.write('.kitchen.travis.yml', kitchen.to_yaml) | |
# Locally we assume it is installed system wide | |
kitchen['driver'].delete('binary') | |
ui.info('Updating .kitchen.local.yml') | |
IO.write('.kitchen.local.yml', kitchen.to_yaml) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment