diff --git a/Berksfile b/Berksfile index 0cd826f..8f58353 100644 --- a/Berksfile +++ b/Berksfile @@ -4,7 +4,11 @@ source 'https://supermarket.chef.io' dashboard database data-processing identity image integration-test network object-storage ops-database ops-messaging orchestration telemetry}.each do |cookbook| - cookbook "openstack-#{cookbook}", github: "stackforge/cookbook-openstack-#{cookbook}", branch: 'master' + if ENV['REPO_DEV'] && Dir.exist?("../cookbook-openstack-#{cookbook}") + cookbook "openstack-#{cookbook}", path: "../cookbook-openstack-#{cookbook}" + else + cookbook "openstack-#{cookbook}", github: "stackforge/cookbook-openstack-#{cookbook}", branch: 'master' + end end cookbook "openstack_client", github: "stackforge/cookbook-openstack-client", branch: 'master' diff --git a/README.md b/README.md index dbbde8a..888e8eb 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ $ sudo su - ```bash # Access the controller as noted above $ source openrc +$ nova-manage version $ nova service-list && nova hypervisor-list $ glance image-list $ keystone user-list @@ -161,6 +162,10 @@ To cleanup everything, use the following rake command. $ chef exec rake clean ``` +## Tools + +See the doc/tools.md for more information. + ## Databags Some basic information about the use of databags within this repo. diff --git a/doc/tools.md b/doc/tools.md new file mode 100644 index 0000000..46a58b1 --- /dev/null +++ b/doc/tools.md @@ -0,0 +1,25 @@ +# Tools + +## Using the test_patch tool + +The tools/test_patch.rb tool will do most of the heavy lifting of testing out one or more patches. +It will basically create a clean environment with a copy of the repo and the patched cookbook, and then run +one of the test environments. + +To see the various options and help: + +```shell +$ chef exec ruby tools\test_patch.rb help test +``` + +## Cookbook Patch Development + +The Berksfile in this repo has been enhanced to look for local working cookbook development directories and use them +to when running the test suite. Simply set REPO_DEV=True and create a directory tree as follows: + +* base_dev_dir\ +** openstack-chef-repo +** cookbook-openstack-*** + +When doing the 'chef exec rake berks_vendor', the Berks file will pick up cookbooks changes from the +cookbook-openstack-*** directories. diff --git a/tools/test_patch.rb b/tools/test_patch.rb new file mode 100644 index 0000000..914b572 --- /dev/null +++ b/tools/test_patch.rb @@ -0,0 +1,188 @@ +# Test Patches +# +# Test patches against the testing repo +# +# Author: Mark Vanderwiel () +# Copyright (c) 2015, IBM, Corp. + +require 'fileutils' +require 'English' +require 'chef/mixin/shell_out' +require 'open3' +require 'thor' + +def version + '0.1.0' +end + +# rubocop:disable LineLength + +def run(command, verbose = true) # rubocop:disable Metrics/MethodLength + puts "## Running command: [#{Dir.pwd}] $ #{command}" + live_stream = STDOUT + live_stream = nil unless verbose + runner = Mixlib::ShellOut.new(command, live_stream: live_stream, timeout: 1800).run_command + runner.error! + runner.stdout +end + +def check_dependencies + puts '## Checking dependencies' + run('chef --version') + run('vagrant --version') +end + +def get_gerrit_user(user) + return user unless user.to_s.empty? + puts '## Using git config to find gitreview.username' + git_config = run('git config -l', false) + /^gitreview.username=(?.*)$/i =~ git_config + abort 'Error! Gerrit user could not be found, please use --username' if user.to_s.empty? + user +end + +def get_patch_info(user, patch) # rubocop:disable Metrics/MethodLength + puts "## Gathering information for patch: #{patch} with user: #{user}" + patch_info = run("ssh -p 29418 #{user}@review.openstack.org gerrit query --current-patch-set #{patch}", false) + /^\s*project: stackforge\/(?.*)$/i =~ patch_info + /^\s*ref: (?.*)$/i =~ patch_info + abort "Error! Patch: #{patch} not valid" if patch_project.nil? + patch_cookbook = patch_project.gsub('cookbook-', '') + puts "## Patch project: #{patch_project}" + puts "## Patch cookbook: #{patch_cookbook}" + puts "## Patch ref: #{patch_ref}" + abort "Error! Only cookbook-openstack-* patches allowed, not from project: #{patch_project}" unless patch_project[/cookbook-openstack-.*/] + { patch: patch, project: patch_project, cookbook: patch_cookbook, ref: patch_ref } +end + +def add_patch(patch_info) + puts "## Adding patch: #{patch_info[:patch]} to cookbook: #{patch_info[:cookbook]}" + run("git clone git@github.com:stackforge/#{patch_info[:project]}.git") + Dir.chdir(patch_info[:project]) do + run("git fetch https://review.openstack.org/stackforge/#{patch_info[:project]} #{patch_info[:ref]}") + run('git checkout FETCH_HEAD') + end +end + +def run_tempest + puts '## Starting tempest tests' + Dir.chdir('vms') do + run("vagrant ssh -c \"sudo bash -c 'cd /opt/tempest && ./run_tests.sh -V'\" controller") + end +end + +def run_basic_queries # rubocop:disable Metrics/MethodLength + puts '## Starting basic query tests' + { + 'nova-manage' => ['version', 'db version'], + 'nova' => %w(--version service-list hypervisor-list net-list image-list), + 'glance-manage' => %w(db_version), + 'glance' => %w(--version image-list), + 'keystone-manage' => %w(db_version), + 'keystone' => %w(--version user-list endpoint-list role-list service-list tenant-list), + 'cinder-manage' => ['version list', 'db version'], + 'cinder' => %w(--version list), + 'rabbitmqctl' => %w(cluster_status) + }.each do |cli, commands| + commands.each do |command| + Dir.chdir('vms') do + run("vagrant ssh -c \"sudo bash -c 'source /root/openrc && #{cli} #{command}'\" controller") + end + end + end + puts '## Finished basic query tests' +end + +def get_test_dir(env, os, patches) + patch = 'master' + patch = patches.gsub(' ', '-') unless patches.to_s.empty? + dir_name = "test-#{env}-#{os}-#{patch}" + puts "## Repo dir: #{dir_name}" + dir_name +end + +# Class to run the CLI +class MyCLI < Thor + desc 'test', 'Spin up test repo with optional patches' + long_desc <<-LONGDESC + This will run the test suite. There are options to include one or more patches using the gerrit review number. + The test will run in a new directory called test---, where aio_nova is the default environment, + ubuntu14 is the default os platform and master (no patches) is the default for patch. + + For patches, the tool will try to find your gerrit review name using the "git config -l". If the tool + cannot automatically find your gerrit review name, use the -u option to specify it.\n + + Examples: + + "$ chef exec ruby test_patch test"\n + This will run the aio_nova against the master branch. + + "$ chef exec ruby test_patch test -t"\n + This will run the aio_nova against the master branch and run the tempest suite. + + "$ chef exec ruby test_patch test -p 161495 -u kramvan"\n + This will run the aio_nova against the master branch including the 161495 patch set. + + "$ chef exec ruby test_patch test -p 161495,123456 -u kramvan"\n + This will run the aio_nova against the master branch including patches 161495 and 123456. + + "$ chef exec ruby test_patch test -p 161495 -i"\n + This will run the aio_nova against the master branch including the 161495 patch set and + run the converge a second time to help check for idempotentcy. Also, in this case the + gerrit username was automatically found. + + "$ chef exec ruby test_patch test -k -s"\n + This is a developer run. The -s will skip the setup steps and just run the converge and tests + again. This allows a developer to tweak and debug a node and then try converge again. + +LONGDESC + option :patches, aliases: :p, default: nil, banner: ' Gerrit patch numbers: xxx,zzz, defaults to no patches just use master branch.' + option :env, aliases: :e, default: 'aio_nova', banner: ' Test environment to run.' + option :os, aliases: :o, default: 'ubuntu14', banner: ' OS to use, ubuntu14 or centos7.' + option :idempotent, aliases: :i, default: false, type: :boolean, banner: ' Run converge a second time to help check for idempotentcy.' + option :query, aliases: :q, default: true, type: :boolean, banner: ' Run basic test queries after converge completes.' + option :tempest, aliases: :t, default: false, type: :boolean, banner: ' Run Tempest suite.' + option :keep, aliases: :k, default: false, type: :boolean, banner: ' Keep the environment, do not run \"rake destroy_machines\".' + option :username, aliases: :u, banner: ' Gerrit user name used to fetch a patch if tool cannot automatically find it in git config.' + option :skip, aliases: :s, banner: ' Skip all source changes, just run converge and tests again. For development after manually tweaking a node.' + def test # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, CyclomaticComplexity, Metrics/PerceivedComplexity + puts "## Starting repo test version: #{version} environment: #{options[:env]} on os: #{options[:os]}" + ENV['REPO_OS'] = options[:os] + + # TODO: Allow more flexibility with dependencies + check_dependencies + + dir_name = get_test_dir(options[:env], options[:os], options[:patches]) + Dir.mkdir(dir_name) unless Dir.exist?(dir_name) + Dir.chdir(dir_name) do + if options[:patches] && !options[:skip] + user = get_gerrit_user(options[:username]) + options[:patches].split(' ').each do |patch| + patch_info = get_patch_info(user, patch) + add_patch(patch_info) + end + end + + run('git clone git@github.com:stackforge/openstack-chef-repo.git') unless options[:skip] + Dir.chdir('openstack-chef-repo') do + ENV['REPO_DEV'] = options[:patches] + run('chef exec rake berks_vendor') unless options[:skip] + + (1..(options[:idempotent] ? 2 : 1)).each do |pass| + puts "## Starting Converge pass: #{pass}" + run("chef exec rake #{options[:env]}") + run_basic_queries if options[:query] + run_tempest if options[:tempest] + puts "## Finished Converge pass: #{pass}" + end + + run('chef exec rake destroy_machines') unless options[:keep] + end + end + + FileUtils.rm_rf(dir_name) unless options[:keep] + puts "## Finished repo test version: #{version} environment: #{options[:env]} on os: #{options[:os]}" + end +end + +MyCLI.start(ARGV)