#!/usr/bin/env ruby # Copyright (C) 2011-2012 RightScale, Inc, All Rights Reserved Worldwide. # # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use, # reproduction, modification, or disclosure of this program is # strictly prohibited. Any use of this program by an authorized # licensee is strictly subject to the terms and conditions, # including confidentiality obligations, set forth in the applicable # License Agreement between RightScale.com, Inc. and # the licensee $stdout.sync = true require File.join(File.dirname(__FILE__), '..', 'lib', 'rconf') module RightConf class Configurer include ProgressReporter def self.run opts = Trollop::options do version "rconf #{VERSION} (c) 2011 RightScale" banner <<-EOS #{DESCRIPTION} Usage: rconf [options] where [options] are: EOS opt :configurators, 'Show available configurators' opt :platform, 'Show current platform' opt :update, 'Update rconf to latest version' opt :remove, 'Remove rconf from all gemsets' opt :config, 'Set path to configuration file', :type => :string opt :output, 'Output file (output to STDOUT by default)', :type => :string opt :reconfigure, 'Bypass all checks and force configuration' opt :force, 'Run rconf even if configuration file has not changed' opt :verbose,'Print debug output' end if opts[:config].nil? && !opts[:configurators] && !opts[:update] && !opts[:remove] opts[:config] = Dir["./*#{CONFIG_EXTENSION}"] if opts[:config].empty? Trollop::die :config, "not used and could not find a '#{CONFIG_EXTENSION}' file in the working directory" else opts[:config] = opts[:config].first end end if opts[:output] begin FileUtils.mkdir_p(File.dirname(opts[:output])) rescue Exception => e Trollop::die :output, "Failed to initialize output file: #{e.message}" end end Command.set_verbose if opts[:verbose] if opts[:configurators] new.list_configurators elsif opts[:platform] new.show_platform elsif opts[:update] new.update elsif opts[:remove] new.remove else new.configure(opts) end end # List all available configurators # # === Return # true:: Always return true def list_configurators puts "The following configurators are registered:\n\n" ConfiguratorRegistry.each do |key, configurator| puts "== #{key} ==".bold puts configurator.desc puts 'Settings:' max_size = configurator.all_settings.keys.map(&:to_s).map(&:size).max configurator.all_settings.each do |name, desc| num_spaces = max_size - name.to_s.size + 1 print " - #{name.to_s.blue}:#{' ' * num_spaces}#{desc}" required_settings = configurator.required_settings || [] if required_settings.include?(name) puts ' [required]'.green else puts end end puts end end # Show current platform as detected by rconf # # === Return # true:: Always return true def show_platform puts "rconf detected the following:\n\n" puts "Family: #{Platform.family}" puts "Flavor: #{Platform.flavor}" puts "Release: #{Platform.release}" end # Update rconf to latest in all installed rubies # # === Return # true:: Always return true def update ProgressReporter.report_to_stdout report_check 'Retrieving latest rconf...' json = Command.execute('curl', '-s', 'http://rubygems.org/api/v1/gems/rconf.json').output json =~ /"version":"([^"]+)"/ version = Regexp.last_match(1) report_fatal 'Failed to retrieve rconf gem information, check network connectivity' unless version report_success report('Latest rconf version is ' + version.blue) update_rconf(version) end # Calls given block with all combination of installed rubies/gemsets # # === Block # Given block should take two arguments: # ruby(String):: Ruby version # gemset(String):: Gemset # # === Return # true:: Always return true def run_in_all_gemsets(&callback) rubies = Command.execute('rvm', 'list').output rubies = rubies.split("\n")[3..-1] rubies.each do |ruby| ruby =~ /(\s+| =>)([^ ]*)\s.*/ ruby = Regexp.last_match(2) gemsets = Command.execute('rvm', ruby, 'exec', 'rvm', 'gemset', 'list').output.split("\n") i = gemsets.index { |gs| gs =~ /^gemsets for #{ruby} / } if i gemsets = gemsets[i + 1..-1] gemsets.each do |gs| gs = gs.lstrip callback.call(ruby, gs) end else report_fatal 'Failed to retrieve installed gemsets' end end end # Update rconf for given rubies if required # # === Parameters # version(String):: Latest version # # === Return # true:: Always return true def update_rconf(version) run_in_all_gemsets do |ruby, gs| report_check("Checking rconf for #{ruby}@#{gs}") rconf = Command.execute('rvm', "#{ruby}@#{gs}", 'gem', 'list', 'rconf').output if rconf =~ /rconf \(#{version}/ report_success elsif rconf =~ /^rconf / report_failure report_check("Updating rconf for #{ruby}@#{gs}") res = Command.execute('rvm', "#{ruby}@#{gs}", 'gem', 'install', 'rconf', '-v', version, '--no-ri', '--no-rdoc') report_result(res.success?) else report('SKIPPED (no rconf)') end end end # Remove rconf from all rubies/gemsets # # === Return # true:: Always return true def remove ProgressReporter.report_to_stdout run_in_all_gemsets do |ruby, gs| rconf = Command.execute('rvm', "#{ruby}@#{gs}", 'gem', 'list', 'rconf').output if rconf =~ /^rconf / report_check("Removing rconf from #{ruby}@#{gs}") res = Command.execute('rvm', "#{ruby}@#{gs}", 'gem', 'uninstall', '-a', '-x', 'rconf') report_result(res.success?) end end end # Actually configure environment # # === Parameters # options[:config](String):: Configuration file # options[:output](String):: Output file, optional # # === Return # true:: Always return true def configure(options) Profile.force_check if options[:force] Profile.force_reconfigure if options[:reconfigure] ProgressReporter.report_to_stdout ProgressReporter.report_to_file(options[:output]) if options[:output] begin lang = Language.load(options[:config]) report_fatal("Validation of configuration file failed:\n - #{lang.validation_errors.map(&:red).join("\n - ")}") unless lang.validation_errors.empty? report_error(lang.warnings.join(', ').green) unless lang.warnings.empty? aborted = false Dir.chdir(File.dirname(options[:config])) do lang.configurators.each do |c| c.run break if aborted = c.aborting c.post_process end end project = File.basename(options[:config], CONFIG_EXTENSION).blue platform = Platform.family.to_s.blue if aborted report("Configuration of #{project} stopped, please follow instructions below to proceed") else report("Successfully configured #{project} for #{platform}") end rescue Exception => e raise if e.is_a?(SystemExit) report_fatal("Execution failed with exception '#{e.message.red}'\n#{e.backtrace.join("\n")}") ensure lang.configurators.each { |c| report("\n!!NOTE: #{c.post_note}\n".green) if c.post_note } end end end end # Yeeeehaaaa! ENV['rvm_interactive_flag'] = '0' # Prevent RVM from re-loading rvmrc trap("INT") { puts "\nAborted!".red; exit 1 } RightConf::Configurer.run