serverspec-runner-1.3.9/0000755000004100000410000000000014346310412015242 5ustar www-datawww-dataserverspec-runner-1.3.9/.travis.yml0000644000004100000410000000022614346310412017353 0ustar www-datawww-datalanguage: ruby rvm: # - 1.9.3 - 2.0.0 - 2.1.5 - 2.2.0 - 2.5.1 before_install: - gem update bundler script: - bundle exec rake spec -I serverspec-runner-1.3.9/.rspec0000644000004100000410000000003714346310412016357 0ustar www-datawww-data--color --format documentation serverspec-runner-1.3.9/README.md0000644000004100000410000001426414346310412016530 0ustar www-datawww-dataserverspec-runner [![Gem Version](https://badge.fury.io/rb/serverspec-runner.svg)](http://badge.fury.io/rb/serverspec-runner) [![BuildStatus](https://secure.travis-ci.org/hiracy/serverspec-runner.png)](http://travis-ci.org/hiracy/serverspec-runner) ====================== Simple execution framework for [serverspec](http://serverspec.org/) run. ---- ## Installation $ gem install serverspec-runner ---- ## Usage initialize spec direcotries and create skeleton-specfiles. $ serverspec-runner -r /path/to/your_serverspec_root Edit your [spec-files](http://serverspec.org/resource_types.html). $ vim /path/to/your_serverspec_root/test_top_dir/.../your_serverspec_test.rb Edit your infrastructure or middleware tests scenario to "[scenario.yml](scenario.yml)". ``` test_top_dir: # test directory top : # test hierarchy directories test_bottom_dir: # test directory bottom - servername # ssh-accessible ip address or fqdn. or alias - : - : : --- servername: # alias name(not required) host: 192.168.0.11 # ssh-accessible ip address or fqdn(required if alias exist) ssh_opts: # ssh options(not required) port: 22 # ssh port option(not required) user: "anyone" # ssh user option(not required) keys: ["~/.ssh/id_rsa"] # ssh private keys option(not required) : # any other Net::SSH Options(not required) any_attribute: "aaa" # host attributes. this example available to get "property[:any_attribute]" from your codes(not required) : : ``` do tests. $ serverspec-runner -r /path/to/your_serverspec_root -s /path/to/your_scenario.yml or $ cd /path/to/your_serverspec_root && serverspec-runner You can also specify [ssh_options.yml](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html)(Net::SSH options) file by "-o" option for default ssh options. $ serverspec-runner -s /path/to/your_scenario.yml -o /path/to/your_ssh_options.yml For example. You write serverspec code like this. ``` require 'spec_helper' describe "nginx" do describe "check running" do describe process('nginx') do it { should be_running } end end describe file('/etc/logrotate.d/nginx') do it { should be_file } it { should contain "rotate 14" } end end ``` You can get the following outputs. * serverspec-runner -t aa : asci-art table(default) ``` +-------------------------------------------+ |description | result | +-------------------------------------------+ |example@anyhost-01(192.168.11.1) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | OK | |example@anyhost-02(192.168.11.2) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | NG | +-------------------------------------------+ ``` * serverspec-runner -t mkd : markdown table format ``` |description | result | |:---------------------------------|:------:| |example@anyhost-01(192.168.11.1) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | OK | |example@anyhost-02(192.168.11.2) | | | nginx | | | check running | | | Process "nginx" | | | should be running | OK | | File "/etc/logrotate.d/nginx" | | | should be file | OK | | should contain "rotate 14" | NG | ``` this example parsed for markdown to that(use -e long option) |description | result | |:----------------------------------------------------------------|:------:| |example@anyhost-01(192.168.11.1) | | | nginx check running Process "nginx" should be running | OK | | nginx File "/etc/logrotate.d/nginx" should be file | OK | | nginx File "/etc/logrotate.d/nginx" should contain "rotate 14" | OK | |example@anyhost-01(192.168.11.2) | | | nginx check running Process "nginx" should be running | OK | | nginx File "/etc/logrotate.d/nginx" should be file | OK | | nginx File "/etc/logrotate.d/nginx" should contain "rotate 14" | NG | * serverspec-runner -t bool : only 'ok' or 'ng' string You can use for cluster monitoring system health. ``` ok ``` ``` ng ``` * serverspec-runner -t csv : CSV file format You can get result CSV format output and can use redirect to file. ``` description,,,,,result example@anyhost-01(192.168.11.1),,,,, ,nginx,,,, ,,check running,,, ,,,Process "nginx",, ,,,,should be running,OK ,,File "/etc/logrotate.d/nginx",,, ,,,should be file,,OK ,,,should contain "rotate 14",,OK example@anyhost-02(192.168.11.2),,,,, ,nginx,,,, ,,check running,,, ,,,Process "nginx",, ,,,,should be running,OK ,,File "/etc/logrotate.d/nginx",,, ,,,should be file,,OK ,,,should contain "rotate 14",,NG ``` For more detail. You can see from `serverspec-runner -h` command. ---- ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request serverspec-runner-1.3.9/bin/0000755000004100000410000000000014346310412016012 5ustar www-datawww-dataserverspec-runner-1.3.9/bin/serverspec-runner0000755000004100000410000000737114346310412021440 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) require 'rubygems' require 'rake' require 'getoptlong' require 'serverspec-runner/version' raketask = 'spec' showtasks = false opts = GetoptLong.new( ["--scenario", "-s", GetoptLong::REQUIRED_ARGUMENT], ["--inventory", "-i", GetoptLong::REQUIRED_ARGUMENT], ["--specroot", "-r", GetoptLong::REQUIRED_ARGUMENT], ["--ssh_options", "-o", GetoptLong::REQUIRED_ARGUMENT], ["--explain", "-e", GetoptLong::REQUIRED_ARGUMENT], ["--tableformat", "-t", GetoptLong::REQUIRED_ARGUMENT], ["--parallel", "-p", GetoptLong::REQUIRED_ARGUMENT], ["--pattern", "-P", GetoptLong::REQUIRED_ARGUMENT], ["--exclude-pattern", "-E", GetoptLong::REQUIRED_ARGUMENT], ["--ignore-error-exit", "-I", GetoptLong::NO_ARGUMENT], ["--activate-specroot", "-a", GetoptLong::NO_ARGUMENT], ["--version", "-v", GetoptLong::NO_ARGUMENT], ["--help", "-h", GetoptLong::NO_ARGUMENT], ["--raketask", "-1", GetoptLong::REQUIRED_ARGUMENT], ["--tmpdir", "-2", GetoptLong::REQUIRED_ARGUMENT], ["--tasks", "-T", GetoptLong::NO_ARGUMENT] ) opts.each do |opt, arg| case opt when '--scenario' ENV['scenario'] = arg when '--inventory' ENV['inventory'] = arg when '--specroot' ENV['specroot'] = arg when '--ssh_options' ENV['ssh_options'] = arg when '--explain' ENV['explain'] = arg when '--tableformat' ENV['tableformat'] = arg when '--parallel' ENV['parallels'] = arg when '--pattern' ENV['pattern'] = arg when '--exclude-pattern' ENV['exclude_pattern'] = arg when '--ignore-error-exit' ENV['ignore_error_exit'] = 'true' when '--activate-specroot' ENV['activate_specroot'] = 'true' when '--tmpdir' ENV['tmpdir'] = arg when '--raketask' raketask = arg when '--tasks' showtasks = true when '--version' puts ServerspecRunner::VERSION exit 0 else puts "Usage: serverspec-runner (options)" puts "-s, --scenario SCENARIO_FILE path to scenario yml file" puts "-i, --inventory INVENTORY_FILE path to ansible inventory yml file" puts "-r, --specroot SPEC_ROOT path to spec tests root dir" puts "-o, --ssh_options SSH_OPTIONS_FILE path to ssh options yml file" puts "-e, --explain (short|long) specify result explain length(default: short)" puts "-t, --tableformat (aa|mkd|csv|bool|none) specify result table type(default: aa)" puts "-T, --tasks display the tasks with descriptions(exec rake -T)" puts "-p, --parallel execute tasks in parallel" puts "-P, --pattern execute pattern in spec directory" puts "-I, --ignore-error-exit exit with 0 even on error" puts "-a, --activate-specroot activate existed spec root only(--specroot is required)" puts "-1, --raketask RAKE_TASK_NAME execute specified rake task only(ex: spec:test::anyhost-01)" puts "-v, --version show version" puts "-h, --help show help" exit 0 end end Rake::TaskManager.record_task_metadata = showtasks load "#{File.dirname(__FILE__)}/../Rakefile" if showtasks mLen = (Rake.application.tasks.max_by { |t| t.name_with_args.size }).name_with_args.size Rake.application.tasks.reject{|t| ['spec:all', 'spec:stdout'].include?(t.name_with_args)}.each do |t| printf("%-#{mLen + 2}s # %s\n", t.name_with_args, t.comment) end exit(0) end Rake::Task[raketask.to_sym].invoke serverspec-runner-1.3.9/spec/0000755000004100000410000000000014346310412016174 5ustar www-datawww-dataserverspec-runner-1.3.9/spec/spec_helper_win.rb0000644000004100000410000000727714346310412021704 0ustar www-datawww-datarequire 'serverspec' require 'pathname' require 'net/ssh' require 'net/ssh/proxy/command' require 'yaml' require 'csv' require 'serverspec-runner/util/hash' require 'winrm' # require extension libraries Dir.glob([ ENV['specroot'] + '/lib/extension/serverspec/**/*.rb', ENV['specroot'] + '/lib/extension/specinfra/**/*.rb']).each {|f| require f} ssh_opts_default = YAML.load_file(ENV['ssh_options']) csv_path = ENV['result_csv'] explains = [] results = [] row_num = [] spacer_char = ' ' unless ENV['tableformat'] == 'csv' spacer_char = ',' if ENV['tableformat'] == 'csv' def get_example_desc(example_group, descriptions) descriptions << example_group[:description] return descriptions if example_group[:parent_example_group] == nil get_example_desc(example_group[:parent_example_group], descriptions) end RSpec.configure do |c| c.expose_current_running_example_as :example c.path = ENV['EXEC_PATH'] run_path = c.files_to_run[0].split('/') speck_i = 0 run_path.reverse.each_with_index do |r,i| if r == 'spec' speck_i = ((run_path.size - 1) - i) end end sliced = run_path.slice((speck_i + 1)..(run_path.size - 2)) role_name = sliced.join('/') 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 set_property (YAML.load_file(ENV['platforms_tmp']))[ENV['TARGET_HOST'].to_sym] if ENV['TARGET_SSH_HOST'] !~ /localhost|127\.0\.0\.1/ c.host = ENV['TARGET_SSH_HOST'] options = Net::SSH::Config.for(c.host, files=["~/.ssh/config"]) ssh_opts ||= ssh_opts_default property[:ssh_opts].each { |k, v| ssh_opts[k.to_sym] = v } if property[:ssh_opts] ssh_opts[:proxy] = Kernel.eval(ssh_opts[:proxy]) if ssh_opts[:proxy] user = options[:user] || ssh_opts[:user] || Etc.getlogin options.merge!(ssh_opts) set :ssh_options, options set :backend, :ssh set :request_pty, true else set :backend, :exec end prev_desc_hierarchy = nil c.before(:suite) do entity_host = (((ENV['TARGET_HOST'] != ENV['TARGET_SSH_HOST']) && (ENV['TARGET_SSH_HOST'] != nil)) ? "(#{ENV['TARGET_SSH_HOST']})" : "") puts "\e[33m" puts "### start [#{role_name}@#{ENV['TARGET_HOST']}] #{entity_host} serverspec... ###" print "\e[m" explains << "#{role_name}@#{ENV['TARGET_HOST']}#{entity_host}" results << "" row_num << 1 end c.after(:each) do if ENV['explain'] == 'long' explains << spacer_char + example.metadata[:full_description] results << (self.example.exception ? 'NG' : 'OK') row_num << 1 else spacer = '' desc_hierarchy = get_example_desc(self.example.metadata[:example_group], []).reverse desc_hierarchy.each_with_index do |ex, i| spacer += spacer_char if prev_desc_hierarchy != nil && prev_desc_hierarchy.length > i && prev_desc_hierarchy[i] == desc_hierarchy[i] else explains << spacer + ex results << '' row_num << i + 1 end end explains << spacer + spacer_char + (self.example.metadata[:description] || '') results << (self.example.exception ? 'NG' : 'OK') row_num << desc_hierarchy.length + 1 prev_desc_hierarchy = desc_hierarchy end end c.after(:suite) do CSV.open(csv_path, 'a') do |writer| explains.each_with_index do |v, i| writer << [v, results[i], row_num[i]] end end end end set :backend, :winrm opts = { user: "Username", # ex.) Administrator password: "Password", # connection password endpoint: "http://IPaddress:5985/wsman", operation_timeout: 300, } winrm = WinRM::Connection.new(opts) Specinfra.configuration.winrm = winrm serverspec-runner-1.3.9/spec/example/0000755000004100000410000000000014346310412017627 5ustar www-datawww-dataserverspec-runner-1.3.9/spec/example/default.rb0000644000004100000410000000077714346310412021613 0ustar www-datawww-datarequire 'spec_helper' describe user('root') do it { should exist } it { should have_uid 0 } it { should have_home_directory '/root' } end describe group('root') do it { should have_gid 0 } end describe 'Filesystem' do describe file('/') do it { should be_mounted } end end describe host('www.google.com') do it { should be_resolvable } it { should be_reachable } end describe command('dmesg | grep "FAIL\|Fail\|fail\|ERROR\|Error\|error"') do its(:exit_status){ should_not eq 0 } end serverspec-runner-1.3.9/spec/example_win/0000755000004100000410000000000014346310412020504 5ustar www-datawww-dataserverspec-runner-1.3.9/spec/example_win/default.rb0000644000004100000410000000052014346310412022452 0ustar www-datawww-datarequire 'spec_helper_win' describe command('hostname') do its(:stdout) { should match /HOSTNAME/ } end describe file('c:/windows') do it { should be_directory } end describe command('tzutil /g') do its(:stdout) { should match /Tokyo Standard Time/ } end describe windows_feature('notepad') do it{ should be_installed } end serverspec-runner-1.3.9/spec/spec_helper.rb0000644000004100000410000000671214346310412021020 0ustar www-datawww-datarequire 'serverspec' require 'pathname' require 'net/ssh' require 'net/ssh/proxy/command' require 'yaml' require 'csv' require 'serverspec-runner/util/hash' # require extension libraries Dir.glob([ ENV['specroot'] + '/lib/extension/serverspec/**/*.rb', ENV['specroot'] + '/lib/extension/specinfra/**/*.rb']).each {|f| require f} ssh_opts_default = YAML.load_file(ENV['ssh_options']) csv_path = ENV['result_csv'] explains = [] results = [] row_num = [] spacer_char = ' ' unless ENV['tableformat'] == 'csv' spacer_char = ',' if ENV['tableformat'] == 'csv' def get_example_desc(example_group, descriptions) descriptions << example_group[:description] return descriptions if example_group[:parent_example_group] == nil get_example_desc(example_group[:parent_example_group], descriptions) end RSpec.configure do |c| c.expose_current_running_example_as :example c.path = ENV['EXEC_PATH'] run_path = c.files_to_run[0].split('/') speck_i = 0 run_path.reverse.each_with_index do |r,i| if r == 'spec' speck_i = ((run_path.size - 1) - i) end end sliced = run_path.slice((speck_i + 1)..(run_path.size - 2)) role_name = sliced.join('/') 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 set_property (YAML.load_file(ENV['platforms_tmp']))[ENV['TARGET_HOST'].to_sym] if ENV['TARGET_CONNECTION'] == 'ssh' || ENV['TARGET_SSH_HOST'] !~ /localhost|127\.0\.0\.1/ c.host = ENV['TARGET_SSH_HOST'] options = Net::SSH::Config.for(c.host, files=["~/.ssh/config"]) ssh_opts ||= ssh_opts_default property[:ssh_opts].each { |k, v| ssh_opts[k.to_sym] = v } if property[:ssh_opts] ssh_opts[:proxy] = Kernel.eval(ssh_opts[:proxy]) if ssh_opts[:proxy] user = options[:user] || ssh_opts[:user] || Etc.getlogin options.merge!(ssh_opts) set :ssh_options, options set :backend, :ssh set :request_pty, true else set :backend, :exec end prev_desc_hierarchy = nil c.before(:suite) do entity_host = (((ENV['TARGET_HOST'] != ENV['TARGET_SSH_HOST']) && (ENV['TARGET_SSH_HOST'] != nil)) ? "(#{ENV['TARGET_SSH_HOST']})" : "") puts "\e[33m" puts "### start [#{role_name}@#{ENV['TARGET_HOST']}] #{entity_host} serverspec... ###" print "\e[m" explains << "#{role_name}@#{ENV['TARGET_HOST']}#{entity_host}" results << "" row_num << 1 end c.after(:each) do if ENV['explain'] == 'long' explains << spacer_char + example.metadata[:full_description] results << (self.example.exception ? 'NG' : 'OK') row_num << 1 else spacer = '' desc_hierarchy = get_example_desc(self.example.metadata[:example_group], []).reverse desc_hierarchy.each_with_index do |ex, i| spacer += spacer_char if prev_desc_hierarchy != nil && prev_desc_hierarchy.length > i && prev_desc_hierarchy[i] == desc_hierarchy[i] else explains << spacer + ex results << '' row_num << i + 1 end end explains << spacer + spacer_char + (self.example.metadata[:description] || '') results << (self.example.exception ? 'NG' : 'OK') row_num << desc_hierarchy.length + 1 prev_desc_hierarchy = desc_hierarchy end end c.after(:suite) do CSV.open(csv_path, 'a') do |writer| explains.each_with_index do |v, i| writer << [v, results[i], row_num[i]] end end end end serverspec-runner-1.3.9/scenario.yml0000644000004100000410000000043114346310412017566 0ustar www-datawww-data# # test_dir_top: # test_dir_second_1: # - ssh-access-host-or-symbol # - : # role_dir_second_2: # - : # : # test_dir_hierarchies: # : # --- # ssh-access-host-or-symbol: # host: xxx.xxx.xxx.xxx # example: - anyhost-01 --- anyhost-01: host: 127.0.0.1 serverspec-runner-1.3.9/.gitignore0000644000004100000410000000032214346310412017227 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp .DS_Store .rvmrc _platforms.yml _serverspec_result.csv serverspec-runner-1.3.9/LICENSE0000644000004100000410000000206014346310412016245 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2014 hiracy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.serverspec-runner-1.3.9/Rakefile0000644000004100000410000002167614346310412016723 0ustar www-datawww-datarequire 'rake' require 'rspec/core/rake_task' require 'yaml' require 'csv' require 'fileutils' require 'net/ssh' require 'open-uri' require 'serverspec-runner' require 'serverspec-runner/util/hash' require 'serverspec-runner/ansible/inventory' desc "Run serverspec to all scenario" task :spec => 'spec:all' namespace :spec do ENV['EXEC_PATH'] = '/usr/local/bin:/usr/sbin:/sbin:/usr/bin:/bin' if ENV['specroot'] == nil if ENV['scenario'] != nil ENV['specroot'] = "#{File.dirname(ENV['scenario'])}" else ENV['specroot'] = '.' end end Dir.chdir(ENV['specroot']) if Dir.exists?(ENV['specroot']) ENV['specpath'] = "#{ENV['specroot']}/spec" ENV['ssh_options'] = ENV['ssh_options'] || "#{ENV['specroot']}/ssh_options_default.yml" || "#{File.dirname(__FILE__)}/ssh_options_default.yml" ENV['ssh_options'] = "#{File.dirname(__FILE__)}/ssh_options_default.yml" unless File.exists?(ENV['ssh_options']) ssh_options = YAML.load_file(ENV['ssh_options']) ENV['result_csv'] = ENV['result_csv'] || './_serverspec_result.csv' csv_file = ENV['result_csv'] CSV.open(csv_file, 'w') { |w| w << ['description', 'result'] } ENV['explain'] = ENV['explain'] || "short" ENV['tableformat'] = ENV['tableformat'] || "aa" ENV['scenario'] = File.expand_path(ENV['scenario'] || "./scenario.yml") ENV['inventory'] = File.expand_path(ENV['inventory']) if ENV['inventory'] def init_specpath(path, only_activate) abs_path = File::expand_path(path) unless only_activate begin print "want to create spec-tree to #{abs_path}? (y/n): " ans = STDIN.gets.strip exit 0 unless (ans == 'y' || ans == 'yes') rescue Exception exit 0 end end FileUtils.mkdir_p("#{path}/lib") FileUtils.cp("#{File.dirname(__FILE__)}/scenario.yml", path) FileUtils.cp("#{File.dirname(__FILE__)}/ssh_options_default.yml", path) FileUtils.cp("#{File.dirname(__FILE__)}/.rspec", path) FileUtils.cp_r("#{File.dirname(__FILE__)}/spec", path) FileUtils.cp_r("#{File.dirname(__FILE__)}/lib/extension", "#{path}/lib") puts("Please edit \"#{abs_path}/scenario.yml\" and change directory to \"#{abs_path}\" and exec \"serverspec-runner\" command !!") end def gen_exec_plan(parent, node, path, ssh_options, tasks, platform) if parent == nil abs_node = node else abs_node = parent[node] end if abs_node.kind_of?(Hash) abs_node.keys.each do |n| path.push(n.to_s) gen_exec_plan(abs_node, n, path, ssh_options, tasks, platform) end path.pop elsif abs_node.kind_of?(Array) abs_node.each do |host_alias| if platform.include?(host_alias.to_sym) ssh_host = platform[host_alias.to_sym][:host] else platform[host_alias.to_sym] = {} ssh_host = host_alias end platform[host_alias.to_sym][:ssh_opts].each { |k, v| ssh_options[k.to_sym] = v } if platform[host_alias.to_sym].include?(:ssh_opts) tasks << "#{path.join('::')}::#{host_alias}" end path.pop end end def exec_tasks(parent, node, real_path, platform) spec_file_pattern = ENV['pattern'] || "**/*.rb" spec_file_exclude_pattern = ENV['exclude_pattern'] if parent == nil abs_node = node else abs_node = parent[node] end if abs_node.kind_of?(Hash) abs_node.keys.each do |n| real_path.push(n) exec_tasks(abs_node, n, real_path, platform) end real_path.pop elsif abs_node.kind_of?(Array) task_path = "#{real_path.join('::')}" abs_node.each do |host_alias| desc "Run serverspec to #{task_path}@#{host_alias}" RSpec::Core::RakeTask.new("#{task_path}::#{host_alias}".to_sym) do |t| fpath = task_path.gsub(/::/, '/') if Dir.exists?("#{ENV['specpath']}/#{fpath}") t.pattern = "#{ENV['specpath']}/#{fpath}/#{spec_file_pattern}" if spec_file_exclude_pattern t.exclude_pattern = "#{ENV['specpath']}/#{fpath}/#{spec_file_exclude_pattern}" end elsif File.file?("#{ENV['specpath']}/#{fpath}.rb") t.pattern = %W[ #{ENV['specpath']}/#{fpath}.rb ] end raise "\e[31mspec file not found!![#{t.pattern.to_s}]\e[m" if Dir.glob(t.pattern).empty? t.fail_on_error = false ENV['TARGET_HOST'] = host_alias ENV['TARGET_SSH_HOST'] = platform[host_alias.to_sym][:host] || host_alias if platform[host_alias.to_sym].key?(:ssh_opts) && platform[host_alias.to_sym][:ssh_opts].key?(:port) ENV['TARGET_CONNECTION'] = 'ssh' end end end real_path.pop end end if !Dir.exists?(ENV['specpath']) init_specpath(ENV['specroot'], false) exit 0 elsif ENV['activate_specroot'] init_specpath(ENV['specroot'], true) exit 0 end if !ENV['tmpdir'] ENV['platforms_tmp'] = "./_platforms.yml" ENV['scenario_tmp'] = "./_scenario.yml" else ENV['platforms_tmp'] = "#{ENV['tmpdir']}/_platforms.yml" ENV['scenario_tmp'] = "#{ENV['tmpdir']}/_scenario.yml" end scenarios = nil platform = {} if ENV['scenario'] =~ /^(http|https):\/\// open(ENV['scenario_tmp'], 'w') do |f| open(ENV['scenario']) do |data| f.write(data.read) end end ENV['scenario'] = ENV['scenario_tmp'] end if !File.file?(ENV['scenario']) && !File.file?("#{ENV['specroot']}/scenario.yml") print "\e[31m" puts "scenario.yml is not found.(--help option can display manual))" print "\e[m" exit 1 end File.open(ENV['scenario'] || "#{ENV['specroot']}/scenario.yml") do |f| YAML.load_stream(f).each_with_index do |data, idx| if idx == 0 scenarios = data else if data != nil data.each do |k, v| platform[k.to_sym] = v.deep_symbolize_keys end end end end end if ENV['inventory'] if !File.file?(ENV['inventory']) print "\e[31m" puts "inventory file is not found.(--help option can display manual))" print "\e[m" exit 1 end platform = Inventory.inventory_to_platform(YAML.load_file(ENV['inventory'])) end if !scenarios print "\e[31m" puts "scenario is empty." print "\e[m" exit 1 end tasks = [] gen_exec_plan(nil, scenarios, [], ssh_options, tasks, platform) task :stdout do ENV['is_example_error'] = 'true' if CSV.foreach(csv_file).any? { |c| c.size > 1 && c[1] == 'NG' } if ENV['tableformat'] == 'none' elsif ENV['tableformat'] == 'bool' ret = 'ok' CSV.foreach(csv_file) do |r| ret = 'ng' if r[1] == 'NG' end puts ret elsif ENV['tableformat'] == 'csv' maxrows = 0 CSV.foreach(csv_file) do |r| maxrows = r[2].to_i if r[2].to_i > maxrows end maxrows += 1 # host row CSV.foreach(csv_file) do |r| pad_comma = ',' * (maxrows - r[0].split(',').length) puts "#{r[0]}#{pad_comma},#{r[1]}" end else maxlen = 0 CSV.foreach(csv_file) do |r| n = r[0].each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+) maxlen = n if n > maxlen end pad_spaces = 4 spacer = nil if ENV['tableformat'] == 'mkd' spacer = "|:" + ("-" * maxlen) + "|:" + ("-" * "result".length) + ":|" elsif ENV['tableformat'] == 'aa' spacer = "+" + ("-" * (maxlen + "result".length + pad_spaces)) + "+" end puts spacer unless ENV['tableformat'] == 'mkd' is_header = true CSV.foreach(csv_file) do |r| n = r[0].each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+) if r[1] == 'OK' s_effect = "\e[32m" e_effect = "\e[m" r[1] = ' ' + r[1] elsif r[1] == 'NG' s_effect = "\e[31m" e_effect = "\e[m" r[1] = ' ' + r[1] end pad_mid = (" " * (maxlen - n)) + " | " pad_tail = (" " * ("result".length - r[1].length)) + " |" puts "|#{s_effect}#{r[0]}#{e_effect}#{pad_mid}#{s_effect}#{r[1]}#{e_effect}#{pad_tail}" if is_header puts spacer is_header = false end end puts spacer unless ENV['tableformat'] == 'mkd' end end task :exit do exit(1) if !ENV['ignore_error_exit'] && ENV['is_example_error'] end exec_tasks = [] if ENV['parallels'] processes = ENV['parallels'].to_i split_group = [] groups = 0 tasks.each_with_index do |t,pos| split_group << t if pos % processes == 0 || pos == tasks.length - 1 multitask "parallel_tasks_#{groups}".to_s => split_group groups += 1 split_group = [] end end groups.times {|i| exec_tasks << "parallel_tasks_#{i}" } else exec_tasks = tasks end exec_tasks << :stdout exec_tasks << :exit task :all => exec_tasks # tempファイルに書き出し open(ENV['platforms_tmp'] ,"w") do |y| YAML.dump(platform, y) end path = {} exec_tasks(nil, scenarios, [], platform) end serverspec-runner-1.3.9/lib/0000755000004100000410000000000014346310412016010 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/serverspec-runner.rb0000644000004100000410000000004414346310412022023 0ustar www-datawww-datarequire 'serverspec-runner/version' serverspec-runner-1.3.9/lib/extension/0000755000004100000410000000000014346310412020024 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/specinfra/0000755000004100000410000000000014346310412021776 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/specinfra/command/0000755000004100000410000000000014346310412023414 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/specinfra/command/linux/0000755000004100000410000000000014346310412024553 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/specinfra/command/linux/base/0000755000004100000410000000000014346310412025465 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/specinfra/command/linux/base/mysql.rb0000644000004100000410000000111014346310412027150 0ustar www-datawww-dataclass Specinfra::Command::Linux::Base::Mysql < Specinfra::Command::Base class << self def check_is_replicated(master=nil, user=nil, password=nil, port=nil) opt_user = "--user=#{user} " || '' opt_password = "--password=#{password} " || '' opt_port = "--port=#{port} " || '' cmd = '' cmd += "echo 'show slave status \\G;' | mysql #{opt_user} #{opt_password} #{opt_port} | " cmd += "grep -e 'Slave_IO_Running: Yes' -e 'Slave_SQL_Running: Yes' -e 'Master_Host: #{master}' | " cmd += "wc -l | grep -w 3" cmd end end end serverspec-runner-1.3.9/lib/extension/specinfra/command/base/0000755000004100000410000000000014346310412024326 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/specinfra/command/base/file.rb0000644000004100000410000000025614346310412025575 0ustar www-datawww-dataclass Specinfra::Command::Base::File < Specinfra::Command::Base class << self def check_is_text(file) "file #{escape(file)} | egrep ' text$'" end end end serverspec-runner-1.3.9/lib/extension/serverspec/0000755000004100000410000000000014346310412022205 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/serverspec/type/0000755000004100000410000000000014346310412023166 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/serverspec/type/mysql.rb0000644000004100000410000000030714346310412024660 0ustar www-datawww-datamodule Serverspec::Type class Mysql < Base def replicated?(master=nil, user=nil, password=nil, port=nil) @runner.check_mysql_is_replicated(master, user, password, port) end end end serverspec-runner-1.3.9/lib/extension/serverspec/type/file.rb0000644000004100000410000000016414346310412024433 0ustar www-datawww-datamodule Serverspec::Type class File < Base def text? @runner.check_file_is_text(@name) end end end serverspec-runner-1.3.9/lib/extension/serverspec/matcher/0000755000004100000410000000000014346310412023630 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/serverspec/matcher/be_replicated.rb0000644000004100000410000000054614346310412026744 0ustar www-datawww-dataRSpec::Matchers.define :be_replicated do match do |host| host.replicated?(@master, @user, @password, @port) end chain :from do |master| @master = master end chain :with_user do |user| @user = user end chain :with_password do |password| @password = password end chain :with_port do |port| @port = port end end serverspec-runner-1.3.9/lib/extension/serverspec/helper/0000755000004100000410000000000014346310412023464 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/extension/serverspec/helper/type.rb0000644000004100000410000000055314346310412024775 0ustar www-datawww-datamodule Serverspec module Helper module Type types = %w( mysql ) types.each {|type| require "extension/serverspec/type/#{type}" } types.each do |type| define_method type do |*args| name = args.first eval "Serverspec::Type::#{type.to_camel_case}.new(name)" end end end end end serverspec-runner-1.3.9/lib/serverspec-runner/0000755000004100000410000000000014346310412021500 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/serverspec-runner/version.rb0000644000004100000410000000006014346310412023506 0ustar www-datawww-datamodule ServerspecRunner VERSION = "1.3.9" end serverspec-runner-1.3.9/lib/serverspec-runner/util/0000755000004100000410000000000014346310412022455 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/serverspec-runner/util/hash.rb0000644000004100000410000000053014346310412023723 0ustar www-datawww-dataclass Hash def depth 1 + (values.map{|v| Hash === v ? v.depth : 1}.max) end def deep_symbolize_keys self.each_with_object({}) do |(k, v), h| if v.is_a?(Array) v = v.map{|vv| vv.deep_symbolize_keys} elsif v.is_a?(Hash) v = v.deep_symbolize_keys end h[k.to_s.to_sym] = v end end end serverspec-runner-1.3.9/lib/serverspec-runner/ansible/0000755000004100000410000000000014346310412023115 5ustar www-datawww-dataserverspec-runner-1.3.9/lib/serverspec-runner/ansible/inventory.rb0000644000004100000410000000301314346310412025474 0ustar www-datawww-datamodule Inventory def self.inventory_to_platform(data) platform = {} data.each do |k, v| v.each do |gk, gv| if gk == 'hosts' if gv.kind_of?(Hash) gv.each do |hk, hv| platform[hk.to_sym] = unless hv.nil? convert_ssh_opt(v['vars']).merge(convert_ssh_opt(hv)) else convert_ssh_opt(v['vars']) end end elsif gv.kind_of?(Array) gv.each do |h| platform[h.to_sym] = convert_ssh_opt(v['vars']) end end end end end platform end private def self.convert_ssh_opt(src) serverspec_opt = {} serverspec_opt[:ssh_opts] = {} src.each do |k, v| case k when 'ansible_host' serverspec_opt[:host] = v when 'ansible_user' serverspec_opt[:ssh_opts][:user] = v when 'ansible_port' serverspec_opt[:ssh_opts][:port] = v when 'ansible_ssh_private_key_file' serverspec_opt[:ssh_opts][:keys] = Array(v) when 'ansible_ssh_pass' serverspec_opt[:ssh_opts][:password] = v when 'ansible_ssh_common_args' if v.include?('StrictHostKeyChecking=no') serverspec_opt[:ssh_opts][:verify_host_key] = false elsif v.include?('UserKnownHostsFile=/dev/null') serverspec_opt[:ssh_opts][:user_known_hosts_file] = '/dev/null' end end end serverspec_opt end end serverspec-runner-1.3.9/Gemfile0000644000004100000410000000014614346310412016536 0ustar www-datawww-datasource "https://rubygems.org" # Specify your gem's dependencies in serverspec-runner.gemspec gemspec serverspec-runner-1.3.9/ssh_options_default.yml0000644000004100000410000000016014346310412022036 0ustar www-datawww-data:port: 22 :paranoid: false #:user: "youre_name" #:keys: ["/path/to/private_key"] #:passphrase: "yourpassphrase" serverspec-runner-1.3.9/serverspec-runner.gemspec0000644000004100000410000000173514346310412022305 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'serverspec-runner/version' Gem::Specification.new do |spec| spec.name = "serverspec-runner" spec.version = ServerspecRunner::VERSION spec.authors = ["hiracy"] spec.email = ["leizhen@mbr.nifty.com"] spec.description = %q{simple execution framework for serverspec} spec.summary = %q{simple execution framework for serverspec} spec.homepage = "https://github.com/hiracy/serverspec-runner" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_runtime_dependency "serverspec" spec.add_runtime_dependency "net-ssh" spec.add_development_dependency "rake" spec.add_development_dependency "rspec-core" spec.add_development_dependency "bundler" end