# 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 # Access to required settings for validation attr_reader :required_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 settings and their descriptions in a hash # # === Parameters # settings(Hash):: Settings descriptions indexed by names # # === Return # true:: Always return true def settings(settings) default_settings = { :only_if => 'Ruby code that should return true for configurator to proceed' } @all_settings = settings.merge(default_settings) true end # Store required settings for validation # # === Parameters # settings(Array):: List of settings that should be checked # # === Return # true:: Always return true def validate_has_settings(*settings) @required_settings = settings.flatten 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 required = self.class.required_settings return nil unless required missing = required.flatten.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 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] 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] = value end end res || @settings_values[meth] 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 }