############################################################################### # # Copyright 2015 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # ############################################################################### # # Rakefile # This file implements the lint and spec tasks for rake so that it will loop # through all of the puppet modules in the deployment/puppet/ folder and run # the respective lint or test tasks for each module. It will then return 0 if # there are issues or return 1 if any of the modules fail. # # Authors: Alex Schultz # Maksim Malchuk # require 'rake' # Use puppet-syntax for the syntax tasks require 'puppet-syntax/tasks/puppet-syntax' PuppetSyntax.exclude_paths ||= [] PuppetSyntax.exclude_paths << "**/spec/fixtures/**/*" PuppetSyntax.exclude_paths << "**/pkg/**/*" PuppetSyntax.exclude_paths << "**/vendor/**/*" PuppetSyntax.fail_on_deprecation_notices = false # Main task list task :default => ["common:help"] task :help => ["common:help"] # RSpec task list desc "Pull down module dependencies, run tests against Git changes and cleanup" task :spec => ["spec:prep", "spec:gemfile", "spec:clean"] task :spec_all => ["spec:all"] task :spec_prep => ["spec:prep"] task :spec_clean => ["spec:clean"] task :spec_standalone => ["spec:gemfile"] # Lint task list # TODO(aschultz): Use puppet-lint for the lint tasks desc "Run lint tasks" task :lint => ["lint:manual"] task :lint_all => ["lint:all"] task :lint_manual => ["lint:manual"] # Syntax task list task :syntax => ["syntax:manifests", "syntax:hiera", "syntax:files", "syntax:templates"] task :syntax_files => ["syntax:files"] # The common tasks is for internal use, so its description is commented namespace :common do # desc "Task to load list of modules to skip" task :load_skip_file, :skip_file do |t, args| def mod(name, options = {}) @modules ||= {} module_name = name.split('/', 2).last @modules[module_name] = options end module_list = [] library_dir = Dir.pwd Dir["#{library_dir}/deployment/**/Puppetfile"].each do |puppetfile| if File.exists?(puppetfile) eval(File.read(puppetfile)) @modules.each { |module_name| module_list << module_name[0] } end end # TODO(aschultz): Fix all modules so they have tests and we no longer need # this file to exclude bad module tests if not args[:skip_file].nil? and File.exists?(args[:skip_file]) File.open(args[:skip_file], 'r').each_line { |line| next if line =~ /^\s*#/ module_list << line.chomp } end $skip_module_list = module_list.uniq end # desc "Task to generate a list of modules with Git changes" task :modulesgit, [:skip_file] do |t, args| args.with_defaults(:skip_file => nil) $module_directories = [] if ENV['SPEC_NO_GIT_MODULES'] $git_module_directories = [] else $git_module_directories = %x[git diff --name-only HEAD~ 2>/dev/null |\ grep -o 'deployment/puppet/[^/]*/' | sort -u].split.select {|mod| File.directory? mod} end if $git_module_directories.empty? Rake::Task["common:modules"].invoke(args[:skip_file]) else Rake::Task["common:load_skip_file"].invoke(args[:skip_file]) $stderr.puts '-'*80 $stderr.puts "Git changes found! Build modules list..." $stderr.puts '-'*80 $git_module_directories.each do |mod| if $skip_module_list.include?(File.basename(mod)) $stderr.puts "Skipping tests... modules.disable_rspec includes #{mod}" next end $module_directories << mod end end end # desc 'Task to generate a list of all modules' task :modules, [:skip_file] do |t, args| args.with_defaults(:skip_file => nil) $module_directories = [] Rake::Task["common:load_skip_file"].invoke(args[:skip_file]) $stderr.puts '-'*80 $stderr.puts "No changes found! Build modules list..." $stderr.puts '-'*80 Dir.glob('deployment/puppet/*') do |mod| next unless File.directory?(mod) if $skip_module_list.include?(File.basename(mod)) $stderr.puts "Skipping tests... modules.disable_rspec includes #{mod}" next end $module_directories << mod end end # desc "Display the list of available rake tasks" task :help do system("rake -T") end end # our spec tasks to loop through the modules and run the 'rake spec' namespace :spec do desc 'Run prep to install gems and pull down module dependencies' task :prep do |t| $stderr.puts '-'*80 $stderr.puts "Install gems and pull down module dependencies..." $stderr.puts '-'*80 library_dir = Dir.pwd ENV['GEM_HOME']="#{library_dir}/.bundled_gems" system("gem install bundler --no-rdoc --no-ri --verbose") system("./deployment/update_modules.sh") end desc 'Remove module dependencies' task :clean do |t| system("./deployment/remove_modules.sh") end desc "Pull down module dependencies, run tests against all modules and cleanup" task :all do |t| Rake::Task["spec:prep"].invoke Rake::Task["common:modules"].invoke('./utils/jenkins/modules.disable_rspec') Rake::Task["spec:gemfile"].invoke Rake::Task["spec:clean"].invoke end desc 'Run spec tasks via module bundler with Gemfile' task :gemfile do |t| if $module_directories.nil? Rake::Task["common:modulesgit"].invoke('./utils/jenkins/modules.disable_rspec') end library_dir = Dir.pwd ENV['GEM_HOME']="#{library_dir}/.bundled_gems" status = true report = {} $module_directories.each do |mod| next unless File.exists?("#{mod}/Gemfile") $stderr.puts '-'*80 $stderr.puts "Running tests for #{mod}" $stderr.puts '-'*80 Dir.chdir(mod) begin result = system("bundle exec rake spec") if !result status = false report[mod] = false $stderr.puts "!"*80 $stderr.puts "Unit tests failed for #{mod}" $stderr.puts "!"*80 else report[mod] = true end rescue Exception => e $stderr.puts "ERROR: Unable to run tests for #{mod}, #{e.message}" status = false end Dir.chdir(library_dir) end if report.any? $stdout.puts '=' * 80 max_module_length = report.keys.max_by { |key| key.length }.length report.each do |mod, success| $stdout.puts "#{mod.ljust(max_module_length)} - #{success.to_s.upcase}" end end $stdout.puts '=' * 80 fail unless status end end # our lint tasks to loop through the modules and run the puppet-lint namespace :lint do desc 'Find all the puppet modules and run puppet-lint on them' task :all do |t| Rake::Task["common:modules"].invoke('./utils/jenkins/modules.disable_rake-lint') Rake::Task["lint:manual"].invoke end desc 'Find the puppet modules with Git changes and run puppet-lint on them' task :manual do |t| if $module_directories.nil? Rake::Task["common:modulesgit"].invoke('./utils/jenkins/modules.disable_rake-lint') end # lint checks to skip if no Gemfile or Rakefile skip_checks = [ "--no-80chars-check", "--no-autoloader_layout-check", "--no-only_variable_string-check", "--no-2sp_soft_tabs-check", "--no-trailing_whitespace-check", "--no-hard_tabs-check", "--no-class_inherits_from_params_class-check", "--with-filename"] $stderr.puts '-'*80 $stderr.puts "Install gems..." $stderr.puts '-'*80 library_dir = Dir.pwd ENV['GEM_HOME']="#{library_dir}/.bundled_gems" system("gem install bundler --no-rdoc --no-ri --verbose") status = true $module_directories.each do |mod| # TODO(aschultz): uncomment this when :rakefile works #next if File.exists?("#{mod}/Rakefile") $stderr.puts '-'*80 $stderr.puts "Running lint for #{mod}" $stderr.puts '-'*80 Dir.chdir(mod) begin result = true Dir.glob("**/**.pp") do |puppet_file| result = false unless system("puppet-lint #{skip_checks.join(" ")} #{puppet_file}") end if !result status = false $stderr.puts "!"*80 $stderr.puts "puppet-lint failed for #{mod}" $stderr.puts "!"*80 end rescue Exception => e $stderr.puts "ERROR: Unable to run lint for #{mod}, #{e.message}" status = false end Dir.chdir(library_dir) end fail unless status end # TODO(aschultz): fix all the modules with Rakefiles to make sure they work # then include this task desc 'Find the puppet modules with Git changes and run lint tasks using existing Gemfile/Rakefile' task :rakefile do |t| Rake::Task["common:modulesgit"].invoke('./utils/jenkins/modules.disable_rake-lint') $stderr.puts '-'*80 $stderr.puts "Install gems..." $stderr.puts '-'*80 library_dir = Dir.pwd ENV['GEM_HOME']="#{library_dir}/.bundled_gems" system("gem install bundler --no-rdoc --no-ri --verbose") status = true $module_directories.each do |mod| next unless File.exists?("#{mod}/Rakefile") $stderr.puts '-'*80 $stderr.puts "Running lint for #{mod}" $stderr.puts '-'*80 Dir.chdir(mod) begin result = system("bundle exec rake lint >/dev/null") $stderr.puts result if !result status = false $stderr.puts "!"*80 $stderr.puts "rake lint failed for #{mod}" $stderr.puts "!"*80 end rescue Exception => e $stderr.puts "ERROR: Unable to run lint for #{mod}, #{e.message}" status = false end Dir.chdir(library_dir) end fail unless status end end # Our syntax checking jobs # The tasks here are an extension on top of the existing puppet helper ones. namespace :syntax do desc 'Syntax check for files/ folder' task :files do |t| $stderr.puts '---> syntax:files' status = true Dir.glob('./files/**/*') do |ocf_file| next if File.directory?(ocf_file) mime_type =`file --mime --brief #{ocf_file}` begin case mime_type.to_s when /shellscript/ result = system("bash -n #{ocf_file}") when /ruby/ result = system("ruby -c #{ocf_file}") when /python/ result = system("python -m py_compile #{ocf_file}") when /perl/ result = system("perl -c #{ocf_file}") else result = true $stderr.puts "Unknown file format, skipping syntax check for #{ocf_file}" end rescue Exception => e result = false $stderr.puts "Checking #{ocf_file} failed with #{e.message}" end if !result status = false $stderr.puts "!"*80 $stderr.puts "Syntax check failed for #{ocf_file}" $stderr.puts "!"*80 end end fail unless status end end def noop(options = {}) require_relative 'tests/noop/fuel-noop-fixtures/noop_tests' ENV['SPEC_ROOT_DIR'] = 'tests/noop/fuel-noop-fixtures' ENV['SPEC_DEPLOYMENT_DIR'] = 'deployment' ENV['SPEC_HIERA_DIR'] = 'tests/noop/fuel-noop-fixtures/hiera' ENV['SPEC_FACTS_DIR'] = 'tests/noop/fuel-noop-fixtures/facts' ENV['SPEC_REPORTS_DIR'] = 'tests/noop/fuel-noop-fixtures/reports' ENV['SPEC_SPEC_DIR'] = 'tests/noop/spec/hosts' ENV['SPEC_TASK_DIR'] = 'deployment/puppet/osnailyfacter/modular' ENV['SPEC_MODULE_PATH'] = 'deployment/puppet' ARGV.shift ARGV.shift if ARGV.first == '--' ENV['SPEC_TASK_DEBUG'] = 'yes' manager = Noop::Manager.new manager.main options end # options can be passed like this: # rake noop:run:all -- -s apache/apache -y neut_tun.ceph.murano.sahara.ceil-controller desc 'Prepare the Noop environment and run all tests' task :noop => %w(noop:setup noop:run:bg noop:show:failed) namespace :noop do desc 'Prepare the Noop tests environment' task :setup do system 'tests/noop/setup_and_diagnostics.sh' end desc 'Run Noop tests self-check' task :test do noop(self_check: true) end task :run => %w(noop:run:bg) namespace :run do desc 'Run all Noop test' task :bg do noop(parallel_run: 'auto', debug: true) end desc 'Run all Noop tests in the foreground' task :fg do noop(parallel_run: 0, debug: true) end desc 'Run only failed tasks' task :failed do noop(run_failed_tasks: true, debug: true) end end task :show => %w(noop:show:all) namespace :show do desc 'Show all task manifests' task :tasks do noop(list_tasks: true) end desc 'Show all Hiera files' task :hiera do noop(list_hiera: true) end desc 'Show all facts files' task :facts do noop(list_facts: true) end desc 'Show all spec files' task :specs do noop(list_specs: true) end desc 'Show the tasks list' task :all do noop(pretend: true) end desc 'Show previous reports' task :reports do noop(load_saved_reports: true) end desc 'Show failed tasks' task :failed do noop(load_saved_reports: true, report_only_failed: true, report_only_tasks: true) end end end