class ServerspecLauncherRakeTasks

loads up the rake tasks

Public Class Methods

load(properties = nil) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 148
def self.load(properties = nil)
  props = PropertiesLoader.new properties
  tasks = ServerspecLauncherRakeTasks.new props.properties
  tasks.load_tasks
end
new(properties = nil) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 21
def initialize(properties = nil)
  @properties = properties
  options = @properties[:options] || {}
  @fail_on_err = options[:fail_on_err]
  @formatters = options[:formatters] || ['tick']
  @colorize = options[:color].nil? ? true : options[:color]
end

Public Instance Methods

debug_tasks() click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 135
def debug_tasks
  namespace :debug do
    desc 'View loaded Shared examples'
    task :shared_examples do
      puts 'Loaded Shared Examples:\n======================='
      load_shared_examples @properties[:shared_example_gems] || []
      shared_examples.each do |ex|
        puts "\t#{ex}"
      end
    end
  end
end
load_tasks() click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 29
def load_tasks
  task serverspec: 'serverspec:all'

  namespace :serverspec do
    targets = @properties[:targets] || {}
    environments = @properties[:environments] || {}
    desc 'Run all targets and environments'

    task all: targets.keys.map { |key| 'serverspec:' + key.to_s.split('.')[0] }.concat(environments.keys.map {|key| 'serverspec:' + key.to_s.split('.')[0]  })
    targets.keys.each do |key|
      target = targets[key]
      process_target(key, target)
    end
    puts



    environments.keys.each do |key|
      desc "Run all tasks in environment #{key}"
      task key.to_sym => "serverspec:#{key}:all"
      namespace key.to_sym do
        environment = environments[key]
        task all: environment[:targets].map { |target, _hash| "serverspec:#{key}:#{target.to_s.split(':')[0].to_sym}" }
        environment[:targets].each do |target, hash|
          process_target("#{target}", hash, 'environment', key.to_s)
        end
      end
    end

  end
end
serverspec_rake_task(host, key, task_name, spec_type, options = {}, target = {}) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 79
def serverspec_rake_task(host, key, task_name, spec_type, options = {}, target = {})
  desc "Run serverspec to #{key}"
  RSpec::Core::RakeTask.new(task_name.to_s.to_sym) do |t|
    ENV['TARGET_HOST'] = host.to_s
    ENV['TARGET'] = key.to_s
    ENV['TASK_NAME'] = task_name.to_s
    ENV['TASK_SOURCE'] = options[:source]
    ENV['TASK_ENV'] = options[:environment]
    t.pattern = "spec/#{spec_type}_spec.rb"
    t.fail_on_error = options[:fail_on_err]
    report_name = options[:environment] ? "reports/#{options[:environment]}/#{key.to_s}/#{host.to_s}" : "reports/#{key.to_s}/#{host.to_s}"
    set_formatters(report_name, options, t)
  end
end
serverspec_task_array(key, spec_type, target, options) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 61
def serverspec_task_array(key, spec_type, target, options)
  env = options[:source] == 'environment' ? ":"+ options[:environment] : ""
  desc "Run serverspec to #{key}"
  task key.to_sym => "serverspec#{env}:#{key}:all"
    namespace key.to_sym do
      desc "Run #{key} against all hosts"
      task :all do
        target[:hosts].each do |host|
          Rake::Task["serverspec#{env}:#{key}:#{host.split(':')[0].to_sym}"].execute
        end
      end
      target[:hosts].each do |host|
        task_name = "#{host || target[:name]}"
        serverspec_rake_task(host, key, task_name, spec_type, options)
      end
    end
end
set_formatters(report_path, options, t) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 94
def set_formatters(report_path, options, t)
  opts = t.rspec_opts
  if options[:formatters].include?('junit') || options[:formatters].include?('xml')
    opts = "#{opts}  --format RspecJunitFormatter --out #{report_path}.xml"
  end
  if options[:formatters].include?('docs') || options[:formatters].include?('documentation') || options[:formatters].include?('docs_file')
    opts = "#{opts}  --format documentation --out #{report_path}.docs"
  end
  if options[:formatters].include?('docs_screen')
    opts = "#{opts}  --format documentation"
  end
  if options[:formatters].include?('tick')
    opts = "#{opts}  --format RspecTickFormatter"
  end
  if options[:formatters].include?('tick_file')
    opts = "#{opts}  --format RspecTickFormatter --out #{report_path}.tick"
  end
  if options[:formatters].include?('progress')
    opts = "#{opts}  --format progress"
  end
  if options[:formatters].include?('html')
    opts = "#{opts}  --format html --out #{report_path}.html"
  end
  if options[:formatters].include?('html_report') || options[:formatters].include?('html_pretty')
    opts = "#{opts}  --format RspecHtmlReporter"
  end
  if options[:formatters].include?('json')
    opts = "#{opts}  --format j --out #{report_path}.json"
  end
  if options[:formatters].include?('launcher')
    opts = "#{opts}  --format LauncherJsonFormatter --out #{report_path}_extended.json"
  end
  unless options[:color]
    opts = "#{opts} --no-color"
  end
  if File.exist?('.rspec')
    opts = "#{opts}  --options .rspec"
  end
  t.rspec_opts = opts
end

Private Instance Methods

chown_files(protocol, target) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 264
def chown_files(protocol, target)
  unless Docker::Image.exist? 'alpine:latest'
    Docker::Image.create('fromImage' => 'alpine:latest')
  end
  command = %W[chown -R #{Etc.getpwnam(ENV['USER']).uid}:#{Etc.getpwnam(ENV['USER']).gid} /share/reports/]
  container = Docker::Container.create(
      'Image' => 'alpine:latest',
      'Mounts' => inspec_mounts(target, protocol),
      'Cmd' => command
  )
  container.start
  container.wait
  container.delete
end
get_inspec_type(target, options) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 204
def get_inspec_type(target, options)
  spec_type = 'role'
  if (target[:control] && target[:profile]) || (target[:control] && target[:spec_type]) || (target[:spec_type] && target[:profile])
    puts 'WARNING: Multiple options specified: they will be evalated in the follow precidence profile > control > spec_type'
  end
  if target[:spec_type]
    spec_type = target[:spec_type]
    options[:spec_type] = 'spec'
  end
  if target[:control]
    spec_type = target[:control]
    options[:spec_type] = 'control'
  end
  if target[:profile]
    spec_type = target[:profile]
    options[:spec_type] = 'control'
  end
  return spec_type, options
end
inspec_commandline(target_info, key, host, spec_type, protocol, options = {}) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 301
def inspec_commandline(target_info, key, host, spec_type, protocol, options = {})
  spec = if options[:spec_type] == 'spec'
    "spec/#{spec_type}_spec.rb"
  else
    spec_type
  end
  target = "#{protocol}://#{protocol == 'local' ? '' : host}"
  command =  %W[exec #{spec} -t #{target}]
  authmethod = target_info[:auth_method] ? target_info[:auth_method] : 'ssh-keys'
  keyfile = target_info[:keyfile] ? target_info[:keyfile] : "#{File.expand_path('~')}/.ssh/id_rsa"
  if protocol == 'ssh' && authmethod == 'ssh-keys'
    command << '-i'
    command << keyfile
  end
  command << "--user=#{target_info[:user]}" if target_info[:user]
  command << "--port=#{target_info[:ssh_port]}" if target_info[:ssh_port]
  command << "--password=#{target_info[:password]}" if target_info[:password]
  command << "--bastion-host=#{target_info[:bastion_host]}" if target_info[:bastion_host]
  command << "--bastion-port=#{target_info[:bastion_port]}" if target_info[:bastion_port]
  command << "--bastion-user=#{target_info[:bastion_user]}" if target_info[:bastion_user]
  command <<  set_inspec_reporters(key, host, options)
  command
end
inspec_environment(target_info, protocol) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 279
def inspec_environment(target_info, protocol)
  environment = []
  environment << "SSH_AUTH_SOCK=#{ENV['SSH_AUTH_SOCK']}" if target_info[:auth_method] == 'agent'
  environment
end
inspec_mounts(target_info, protocol) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 285
def inspec_mounts(target_info, protocol)
  mounts = [{
       'Type' => 'bind',
       'Source' => "#{Dir.pwd}",
       'Target' => "/share"
   }, {
       'Type' => 'bind',
       'Source' => "/etc/hosts",
       'Target' => "/etc/hosts"
   }]
  mount_ssh = target_info[:mount_ssh_dir] ? target_info[:mount_ssh_dir] : true
  mounts <<  { 'Type' => 'bind', 'Source' => "#{File.expand_path('~')}/.ssh", 'Target' => "#{File.expand_path('~')}/.ssh" } if mount_ssh && protocol == 'ssh'
  mounts <<  { 'Type' => 'bind', 'Source' => "#{ENV['SSH_AUTH_SOCK']}", 'Target' => "#{ENV['SSH_AUTH_SOCK']}" } if target_info[:auth_method] == 'agent'
  mounts
end
inspec_target(key, options, target) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 171
def inspec_target(key, options, target)
  spec_type, options = get_inspec_type(target, options)
  if target[:hosts].is_a?(Array)
    inspec_task_array(key, spec_type, target, options)
  elsif target[:hosts]
    host = target[:hosts]
    task_name = (key || target[:name]).to_s
    inspec_task(host, "#{key}/#{host}", task_name, spec_type, options, target)
  else
    host = (target[:hosts] || 'local' )
    task_name = (key || target[:name]).to_s
    inspec_task(host, "#{key}/#{host}", task_name, spec_type, options, target)
  end
end
inspec_task(host, key, task_name, spec_type, options = {}, target = {}) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 224
def inspec_task(host, key, task_name, spec_type, options = {}, target = {})
  protocol = host == 'local' ? 'local' : 'ssh'
  command = inspec_commandline(target,key,host, spec_type, protocol, options)
  Rake::Task.define_task(task_name.to_s.to_sym) do

    unless Docker::Image.exist? 'chef/inspec:latest'
      Docker::Image.create('fromImage' => 'chef/inspec:latest')
    end

    container = Docker::Container.create(
        'Image' => 'chef/inspec:latest',
        'Mounts' => inspec_mounts(target, protocol),
        'Cmd' => command,
        'Tty' => STDIN.tty?,
        'Env' => inspec_environment(target, protocol)
    )
    begin
      puts "inspec #{command.join(' ')}"
      networks = target[:attach_to] || []
      networks.each do |nw|
        network = Docker::Network.get(nw)
        network.connect(container.id)  
      end
      container.start
      container.wait
    rescue Docker::Error::TimeoutError => ex
      container.stop
      puts "A Docker::Error::TimeoutError occurred, most likey because you are using password protected ssh key, whihc is not supported"
      puts "Either agent your ssh key and use 'agent' as the auth_method in your target settings or use an unprotected key (not recommended)"
      puts 'Container Logs: '
      puts container.logs(stderr: true)
    ensure
      puts container.logs(stdout: true)
      container.delete
      #Report have wrong permission hookie fix
      chown_files(protocol, target)
    end
  end
end
inspec_task_array(key, spec_type, target, options) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 186
def inspec_task_array(key, spec_type, target, options)
  env = options[:source] == 'environment' ? ":"+ options[:environment] : ""
  desc "Run serverspec to #{key}"
  task key.to_sym => "serverspec#{env}:#{key}:all"
  namespace key.to_sym do
    desc "Run #{key} against all hosts"
    task :all do
      target[:hosts].each do |host|
        Rake::Task["serverspec#{env}:#{key}:#{host.split(':')[0].to_sym}"].execute
      end
    end
    target[:hosts].each do |host|
      task_name = "#{host || target[:name]}"
      inspec_task(host, "#{key}/#{host}", task_name, spec_type, options, target)
    end
  end
end
process_target(key, target, task_source = 'target', environment = nil) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 156
def process_target(key, target, task_source = 'target', environment = nil)
  options = {
      fail_on_err: target[:fail_on_err] || @fail_on_err,
      formatters: target[:formatters] || @formatters,
      color: target[:color].nil? ? @colorize : target[:color],
      source: task_source,
      environment: environment
  }
  if target[:backend] == 'inspec'
    inspec_target(key, options, target)
  else
    serverspec_target(key, options, target)
  end
end
serverspec_target(key, options, target) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 340
def serverspec_target(key, options, target)
  spec_type = target[:spec_type] || 'role'
  if target[:hosts].is_a?(Array)
    serverspec_task_array(key, spec_type, target, options)
  elsif target[:hosts]
    host = target[:hosts]
    task_name = (key || target[:name]).to_s
    serverspec_rake_task(host, key, task_name, spec_type, options)
  else
    task_name = (key || target[:name]).to_s
    serverspec_rake_task(key, key, task_name, spec_type, options)
  end
end
set_inspec_reporters(key, host, options) click to toggle source
# File lib/serverspec_launcher/rake_tasks.rb, line 325
def set_inspec_reporters(key, host, options)
  reporters = []
  report_path = options[:environment] ? "/share/reports/#{options[:environment]}/#{key}" :  "/share/reports/#{key}"
  reporters <<  "junit:#{report_path}.xml" if options[:formatters].include?('junit') || options[:formatters].include?('xml')
  reporters <<  "documentation:#{report_path}.docs" if options[:formatters].include?('docs') || options[:formatters].include?('documentation') || options[:formatters].include?('docs_file')
  reporters <<  'documentation' if options[:formatters].include?('docs_screen')
  reporters <<  'cli' if options[:formatters].include?('tick')
  reporters <<  "cli:#{report_path}.tick" if options[:formatters].include?('tick_file')
  reporters <<  'progress' if options[:formatters].include?('progress')
  reporters <<  "html:#{report_path}.html" if options[:formatters].include?('html')
  reporters <<  "html#{report_path}.html" if (options[:formatters].include?('html_report') || options[:formatters].include?('html_pretty')) && !options[:formatters].include?('html')
  reporters <<  "json-rspec:#{report_path}.json" if options[:formatters].include?('json')
  "--reporter=#{reporters.join(' ')}"
end