=begin rdoc The Provisioner is responsible for provisioning REMOTE servers This class only comes in to play when calling the setup commands on the development machine =end module PoolParty module Provisioner # Provision master # Convenience method to clean def self.provision_master(cloud, testing=false) Provisioner::Master.new(cloud).process_install!(testing) end def self.configure_master(cloud, testing=false) Provisioner::Master.new(cloud).process_configure!(testing) end def self.reconfigure_master(cloud, testing=false) Provisioner::Master.new(cloud).process_reconfigure!(testing) end def self.provision_slaves(cloud, testing=false) cloud.nonmaster_nonterminated_instances.each do |sl| provision_slave(sl, cloud, testing) end end def self.configure_slaves(cloud, testing=false) cloud.nonmaster_nonterminated_instances.each do |sl| configure_slave(sl, cloud, testing) end end def self.provision_slave(instance, cloud, testing=false) Provisioner::Slave.new(instance, cloud).process_install!(testing) end def self.configure_slave(instance, cloud, testing=false) Provisioner::Slave.new(instance, cloud).process_configure!(testing) end def self.process_clean_reconfigure_for!(instance, cloud, testing=false) Provisioner::Master.new(cloud).process_clean_reconfigure_for!(instance, testing) end def self.clear_master_ssl_certs(cloud, testing=false) Provisioner::Master.new(cloud).clear_master_ssl_certs end class ProvisionerBase include Configurable include CloudResourcer def initialize(instance,cloud=self, os=:ubuntu) @instance = instance @cloud = cloud options(cloud.options) if cloud && cloud.respond_to?(:options) set_vars_from_options(instance.options) unless instance.nil? || !instance.options || !instance.options.empty? options(instance.options) if instance.respond_to?(:options) @os = os.to_s.downcase.to_sym loaded end # Callback after initialized def loaded(opts={}, parent=self) end ### Installation tasks # This is the actual runner for the installation def install valid? ? install_string : error end # Write the installation tasks to a file in the storage directory def write_install_file error unless valid? ::FileUtils.mkdir_p Base.storage_directory unless ::File.exists?(Base.storage_directory) provisioner_file = ::File.join(Base.storage_directory, "install_#{name}.sh") ::File.open(provisioner_file, "w+") {|f| f << install } end def name @instance.name end # TODO: Clean up this method def process_install!(testing=false) error unless valid? write_install_file setup_runner(@cloud) unless testing vputs "Logging on to #{@instance.ip} (#{@instance.name})" @cloud.rsync_storage_files_to(@instance) vputs "Preparing configuration on the master" before_install(@instance) process_clean_reconfigure_for!(@instance, testing) vputs "Logging in and running provisioning on #{@instance.name}" cmd = "cd #{Base.remote_storage_path} && chmod +x install_#{name}.sh && /bin/sh install_#{name}.sh; rm install_#{name}.sh" verbose ? @cloud.run_command_on(cmd, @instance) : hide_output {@cloud.run_command_on(cmd, @instance)} process_clean_reconfigure_for!(@instance, testing) after_install(@instance) end end # Install callbacks # Before installation callback def before_install(instance) end def after_install(instance) end ### Configuraton tasks def configure valid? ? configure_string : error end def write_configure_file error unless valid? provisioner_file = ::File.join(Base.storage_directory, "configure_#{name}.sh") ::File.open(provisioner_file, "w+") {|f| f << configure } end def process_configure!(testing=false) error unless valid? write_configure_file setup_runner(@cloud) unless testing vputs "Logging on to #{@instance.ip}" @cloud.rsync_storage_files_to(@instance) cmd = "cd #{Base.remote_storage_path} && chmod +x configure_#{name}.sh && /bin/sh configure_#{name}.sh && rm configure_#{name}.sh" verbose ? @cloud.run_command_on(cmd, @instance) : hide_output {@cloud.run_command_on(cmd, @instance)} end end def process_clean_reconfigure_for!(instance, testing=false) vputs "Cleaning certs from master: #{instance.name}" # puppetca --clean #{instance.name}.compute-1.internal; puppetca --clean #{instance.name}.ec2.internal # find /etc/puppet/ssl -type f -exec rm {} \; unless testing # @cloud.run_command_on("rm -rf /etc/puppet/ssl", instance) unless instance.master? str = returning String.new do |s| s << "puppetca --clean #{instance.name}.compute-1.internal 2>&1 > /dev/null;" s << "puppetca --clean #{instance.name}.ec2.internal 2>&1 > /dev/null" end @cloud.run_command_on(str, @cloud.master) end end def clear_master_ssl_certs str = returning String.new do |s| s << "puppetca --clean master.compute-1.internal 2>&1 > /dev/null;" s << "puppetca --clean master.ec2.internal 2>&1 > /dev/null" end @cloud.run_command_on("if [ -f '/usr/bin/puppetcleaner' ]; then /usr/bin/env puppetcleaner; else #{str}; fi", @cloud.master) end def process_reconfigure!(testing=false) @cloud.run_command_on(PoolParty::Remote::RemoteInstance.puppet_runner_command, @instance) unless testing end # Tasks that need to be performed everytime we do any # remote ssh'ing into any instance def setup_runner(force=false) @cloud.prepare_to_configuration @cloud.build_and_store_new_config_file(force) end def valid? true end def error "Error in installation" end # Gather all the tasks into one string def install_string (default_install_tasks).each do |task| case task.class when String task when Method self.send(task.to_sym) end end.nice_runnable end def configure_string (default_configure_tasks).each do |task| case task.class when String task when Method self.send(task.to_sym) end end.nice_runnable end # Tasks with default tasks # These are run on all the provisioners, master or slave def default_install_tasks [ "#!/usr/bin/env sh", upgrade_system, fix_rubygems, make_logger_directory, install_puppet, custom_install_tasks ] << install_tasks end # Tasks with default configuration tasks # This is run on the provisioner, regardless def default_configure_tasks [ custom_configure_tasks ] << configure_tasks end # Build a list of the tasks to run on the instance def install_tasks(a=[]) @install_task ||= a end def configure_tasks(a=[]) @configure_tasks ||= a end # Custom installation tasks # Allow the remoter bases to attach their own tasks on the # installation process def custom_install_tasks @cloud.custom_install_tasks_for(@instance) || [] end # Custom configure tasks # Allows the remoter bases to attach their own # custom configuration tasks to the configuration process def custom_configure_tasks @cloud.custom_configure_tasks_for(@instance) || [] end # Get the packages associated with each os def puppet_packages case @os when :fedora "puppet-server puppet factor" else "puppet puppetmaster" end end # Package installers for general *nix operating systems def self.installers @installers ||= { :ubuntu => "aptitude install -y", :fedora => "yum install", :gentoo => "emerge" } end # Convenience method to grab the installer def installer_for(names=[]) packages = names.is_a?(Array) ? names.join(" ") : names "#{self.class.installers[@os]} #{packages}" end # Install from the class-level def self.install(instance, cl=self) new(instance, cl).install end def self.configure(instance, cl=self) new(instance, cl).configure end # Template directory from the provisioner base def template_directory File.join(File.dirname(__FILE__), "..", "templates") end def fix_rubygems "echo '#{open(::File.join(template_directory, "gem")).read}' > /usr/bin/gem" end def create_local_node str = <<-EOS node default { include poolparty } EOS @cloud.list_of_running_instances.each do |ri| str << <<-EOS node "#{ri.name}" {} EOS end "echo '#{str}' > /etc/puppet/manifests/nodes/nodes.pp" end def upgrade_system case @os when :ubuntu " if grep -q 'http://mirrors.kernel.org/ubuntu hardy main universe' /etc/apt/sources.list then echo 'Updated already' else touch /etc/apt/sources.list echo 'deb http://mirrors.kernel.org/ubuntu hardy main universe' >> /etc/apt/sources.list aptitude update -y #{unix_hide_string} <<heredoc Y heredoc aptitude autoclean #{unix_hide_string} fi " else "# No system upgrade needed" end end def install_puppet "#{installer_for( puppet_packages )}" end def make_logger_directory "mkdir -p /var/log/poolparty" end def create_poolparty_manifest <<-EOS cp #{Base.remote_storage_path}/poolparty.pp /etc/puppet/manifests/classes EOS end end end end ## Load the provisioners Dir[File.dirname(__FILE__) + "/provisioners/*.rb"].each do |file| require file end