# 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 require 'digest/sha1' module RightConf # Configurator mixin, defines DSL and common validation method module Configurator include ProgressReporter module ClassMethods # Key associated with configurator attr_reader :key # Description attr_reader :desc # Access to settings for documentation attr_reader :all_settings # Associate configurator with given key # # === Parameters # key(Symbol):: Key configurator should be associated with # # === Return # true:: Always return true def register(key) ConfiguratorRegistry[key] = self @key = key true end # Store description for documentation # # === Parameters # description(String):: Description # # === Return # true:: Always return true def description(description) @desc = description true end # Store setting definition # A definition consists of a name, a description and optionally options # Possible options are: # :required => true|false Whether the setting is required # # === Parameters # settings(Hash):: Settings descriptions indexed by names # # === Return # true:: Always return true def setting(name, description, options={}) @all_settings ||= [{ :name => 'only_if', :description => 'Ruby code that should return true for configurator to proceed', :options => {} }] @all_settings << { :name => name, :description => description, :options => options || {} } true end end # Extend base class with ClassMethods module methods # # === Parameters # base(Object):: Object including module def self.included(base) base.__send__(:extend, ClassMethods) end # Check whether configurator has values for all required settings # # === Return # nil:: If settings are valid for this configurator # error(String):: Error message otherwise def validate @settings_values.merge!(OverridesLanguage.overrides_for(self.class.key.to_s)) if e = OverridesLanguage.parse_error error = "Could not load overrides file '#{OverridesLanguage::OVERRIDES_FILE}' (#{e.message})" else required = self.class.all_settings.select { |s| s[:options][:required] }.map { |s| s[:name] } return nil unless required missing = required.select { |s| !@settings_values.include?(s) } error = case missing.size when 0 then nil when 1 then "Required setting #{missing.first} is " else "Required settings #{missing.join(', ')} are " end error += "missing for configuration section '#{self.class.key}'" if error end error end # Check system to determine whether configurator needs to run # # === Return # true:: If configurator needs to run # false:: Otherwise def check Platform.dispatch { :check } end # Run configurator for current platform # # === Parameters # args:: Pass-through arguments, given to platform specific implementation # # === Return # true:: Always return true def run(*args) key = "#{self.class.key}-#{@index}" sha = Profile.configurator_signature(key) sig = signature must_configure = Profile.force_reconfigure? if !must_configure && (only_if.nil? || instance_eval(only_if)) must_configure = (Profile.force_check? || sha != sig) && !check end Platform.dispatch(*args) { :run } if must_configure Profile.set_configurator_signature(key, sig) unless aborting true end # Called even if configuration is already done for steps that must # always happen, do nothing by default # # === Return # true:: Always return true def post_process true end # Calculate unique SHA for current settings # # === Return # sha(String):: SHA for current settings def signature blob = VERSION blob = @settings_values.inject(blob) { |b, (k, v)| b += "#{k}:#{v};" } if @settings_values sha = Digest::SHA1.hexdigest(blob) end # Get value of configuration option # # === Parameters # config_option(Symbol):: Configuration option to return # # === Returns # value:: Value of configuration option if there is one # nil:: Otherwise def [](config_option) @settings_values[config_option.to_s] end protected # DSL implementation, set settings value if arguments, get it otherwise. # # === Parameters # meth(Symbol):: Method symbol # args(Array):: List of arguments # # === Return # res(Object):: Configuration setting value or setter return value if # arguments def method_missing(meth, *args) num_args = args.length res = nil if num_args > 0 meth = $1.to_sym unless (meth.to_s =~ /(.+)=$/).nil? value = num_args == 1 ? args[0] : args method_name = meth.id2name if self.public_methods.include?("#{method_name}=") res = self.send("#{method_name}=", value) else res = @settings_values[meth.to_s] = value end end res || @settings_values[meth.to_s] end # Initialize configuration settings hash and index # # === Parameters # index(Fixnum):: Unique index of configurators in rconf file # # === Return # true:: Always return true def initialize(index) @index = index @settings_values ||= Hash.new true end end end # Load all configurators Dir[File.join(File.dirname(__FILE__), 'support', '*.rb')].each { |c| require c } Dir[File.join(File.dirname(__FILE__), 'configurators', '*.rb')].each { |c| require c }