Last active
January 20, 2016 20:59
-
-
Save coderanger/d5d762e99bba9c691099 to your computer and use it in GitHub Desktop.
WIP knife travis plugin.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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