lib/rubber/recipes/rubber/setup.rb in sml-rubber-0.9.13 vs lib/rubber/recipes/rubber/setup.rb in sml-rubber-1.5.5

- old
+ new

@@ -2,20 +2,48 @@ desc <<-DESC Bootstraps instances by setting timezone, installing packages and gems DESC task :bootstrap do - set_timezone link_bash + set_timezone + enable_multiverse upgrade_packages install_packages setup_volumes setup_gem_sources install_gems deploy.setup end + # Sets up instance to allow root access (e.g. recent canonical AMIs) + def enable_root_ssh(ip, initial_ssh_user) + old_user = user + begin + set :user, initial_ssh_user + + task :_allow_root_ssh, :hosts => ip do + rsudo "cp /home/#{initial_ssh_user}/.ssh/authorized_keys /root/.ssh/" + end + + begin + _allow_root_ssh + rescue ConnectionError => e + if e.message =~ /Net::SSH::AuthenticationFailed/ + logger.info "Can't connect as user #{initial_ssh_user} to #{ip}, assuming root allowed" + else + sleep 2 + logger.info "Failed to connect to #{ip}, retrying" + retry + end + end + ensure + set :user, old_user + end + + end + desc <<-DESC Sets up aliases for instance hostnames based on contents of instance.yml. Generates /etc/hosts for local/remote machines and sets hostname on remote instances, and sets values in dynamic dns entries DESC @@ -60,10 +88,87 @@ update_dyndns(ic) end end desc <<-DESC + Sets up the additional dns records supplied in the dns_records config in rubber.yml + DESC + required_task :setup_dns_records do + records = rubber_env.dns_records + if records && rubber_env.dns_provider + provider = Rubber::Dns::get_provider(rubber_env.dns_provider, rubber_env) + + # collect the round robin records (those with the same host/domain/type) + rr_records = [] + records.each_with_index do |record, i| + m = records.find_all {|r| record['host'] == r['host'] && record['domain'] == r['domain'] && record['type'] == r['type']} + m = m.sort {|a,b| a.object_id <=> b.object_id} + rr_records << m if m.size > 1 && ! rr_records.include?(m) + end + + # simple records are those that aren't round robin ones + simple_records = records - rr_records.flatten + + # for each simple record, create or update as necessary + simple_records.each do |record| + matching = provider.find_host_records(:host => record['host'], :domain =>record['domain'], :type => record['type']) + if matching.size > 1 + msg = "Multiple records in dns provider, but not in rubber.yml\n" + msg << "Round robin records need to be in both, or neither.\n" + msg << "Please fix manually:\n" + msg << matching.pretty_inspect + fatal(msg) + end + + record = provider.setup_opts(record) + if matching.size == 1 + match = matching.first + if provider.host_records_equal?(record, match) + logger.info "Simple dns record already up to date: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}" + else + logger.info "Updating simple dns record: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}" + provider.update_host_record(match, record) + end + else + logger.info "Creating simple dns record: #{record[:host]}.#{record[:domain]}:#{record[:type]} => #{record[:data]}" + provider.create_host_record(record) + end + end + + # group round robin records + rr_records.each do |rr_group| + host = rr_group.first['host'] + domain = rr_group.first['domain'] + type = rr_group.first['type'] + matching = provider.find_host_records(:host => host, :domain => domain, :type => type) + + # remove from consideration the local records that are the same as remote ones + matching.clone.each do |r| + rr_group.delete_if {|rg| provider.host_records_equal?(r, rg) } + matching.delete_if {|rg| provider.host_records_equal?(r, rg) } + end + if rr_group.size == 0 && matching.size == 0 + logger.info "Round robin dns records already up to date: #{host}.#{domain}:#{type}" + end + + # create the local records that don't exist remotely + rr_group.each do |r| + r = provider.setup_opts(r) + logger.info "Creating round robin dns record: #{r[:host]}.#{r[:domain]}:#{r[:type]} => #{r[:data]}" + provider.create_host_record(r) + end + + # remove the remote records that don't exist locally + matching.each do |r| + logger.info "Removing round robin dns record: #{r[:host]}.#{r[:domain]}:#{r[:type]} => #{r[:data]}" + provider.destroy_host_record(r) + end + end + end + end + + desc <<-DESC Sets up aliases for instance hostnames based on contents of instance.yml. Generates /etc/hosts for remote machines and sets hostname on remote instances DESC task :setup_remote_aliases do hosts_file = '/etc/hosts' @@ -85,11 +190,13 @@ filtered = filtered + remote_hosts # Put the generated hosts back on remote instance put filtered, hosts_file # Setup hostname on instance so shell, etcs have nice display - sudo "echo $CAPISTRANO:HOST$ > /etc/hostname && hostname $CAPISTRANO:HOST$" + rsudo "echo $CAPISTRANO:HOST$ > /etc/hostname && hostname $CAPISTRANO:HOST$" + # Newer ubuntus ec2-init script always resets hostname, so prevent it + rsudo "mkdir -p /etc/ec2-init && echo compat=0 > /etc/ec2-init/is-compat-env" end # TODO # /etc/resolv.conf to add search domain # ~/.ssh/options to setup user/host/key aliases @@ -140,63 +247,90 @@ task :install_gems do gem_helper(false) end desc <<-DESC + Install ruby gems defined in Gemfile + DESC + after "deploy:update_code", "rubber:install_bundler_gems" if Rubber::Util.is_bundler? + task :install_bundler_gems do + rsudo "cd #{current_release} && RAILS_ENV=#{RUBBER_ENV} bundle install --without development test" + end + + desc <<-DESC Install ruby gems defined in the rails environment.rb DESC - after "deploy:symlink", "rubber:install_rails_gems" if Rubber::Util.is_rails? + after "rubber:config", "rubber:install_rails_gems" if (Rubber::Util::is_rails2? && !Rubber::Util.is_bundler?) task :install_rails_gems do - sudo "sh -c 'cd #{current_path} && RAILS_ENV=#{RUBBER_ENV} rake gems:install'" + rsudo "cd #{current_release} && RAILS_ENV=#{RUBBER_ENV} rake gems:install" end desc <<-DESC - Setup ruby gems sources. Set 'gemsources' in rubber.yml to \ - be an array of URI strings. + Convenience task for installing your defined set of ruby gems locally. DESC - task :setup_gem_sources do - if rubber_env.gemsources - script = prepare_script 'gem_sources_helper', <<-'ENDSCRIPT' - ruby - $@ <<-'EOF' + required_task :install_local_gems do + fatal("install_local_gems can only be run in development") if RUBBER_ENV != 'development' + env = rubber_cfg.environment.bind(rubber_cfg.environment.known_roles) + gems = env['gems'] + expanded_gem_list = [] + gems.each do |gem_spec| + if gem_spec.is_a?(Array) + expanded_gem_list << "#{gem_spec[0]}:#{gem_spec[1]}" + else + expanded_gem_list << gem_spec + end + end + expanded_gem_list = expanded_gem_list.join(' ') - sources = ARGV + logger.info "Installing gems:#{expanded_gem_list}" + open("/tmp/gem_helper", "w") {|f| f.write(gem_helper_script)} + system "ruby /tmp/gem_helper install #{expanded_gem_list}" + end - installed = [] - `gem sources -l`.grep(/^[^*]/) do |line| - line = line.strip - installed << line if line.size > 0 - end + set :gem_sources_helper_script, <<-'ENDSCRIPT' + sources = ARGV - to_install = sources - installed - to_remove = installed - sources + installed = [] + `gem sources -l`.each_line do |line| + line = line.strip + installed << line if line.size > 0 && line =~ /^[^*]/ + end - if to_install.size > 0 - to_install.each do |source| - system "gem sources -a #{source}" - fail "Unable to add gem sources" if $?.exitstatus > 0 - end - end - if to_remove.size > 0 - to_remove.each do |source| - system "gem sources -r #{source}" - fail "Unable to remove gem sources" if $?.exitstatus > 0 - end - end + to_install = sources - installed + to_remove = installed - sources - 'EOF' - ENDSCRIPT - - sudo "sh #{script} #{rubber_env.gemsources.join(' ')}" + if to_install.size > 0 + to_install.each do |source| + system "gem sources -a #{source}" + fail "Unable to add gem sources" if $?.exitstatus > 0 + end end + if to_remove.size > 0 + to_remove.each do |source| + system "gem sources -r #{source}" + fail "Unable to remove gem sources" if $?.exitstatus > 0 + end + end + ENDSCRIPT + + desc <<-DESC + Setup ruby gems sources. Set 'gemsources' in rubber.yml to \ + be an array of URI strings. + DESC + task :setup_gem_sources do + if rubber_env.gemsources + script = prepare_script 'gem_sources_helper', gem_sources_helper_script, nil + rsudo "ruby #{script} #{rubber_env.gemsources.join(' ')}" + end end desc <<-DESC The ubuntu has /bin/sh linking to dash instead of bash, fix this You can override this task if you don't want this to happen DESC task :link_bash do - sudo("ln -sf /bin/bash /bin/sh") + rsudo "ln -sf /bin/bash /bin/sh" end desc <<-DESC Set the timezone using the value of the variable named timezone. \ Valid options for timezone can be determined by the contents of \ @@ -206,16 +340,34 @@ directory and file as the value. For example 'Africa/Abidjan' or \ 'posix/GMT' or 'Canada/Eastern'. DESC task :set_timezone do opts = get_host_options('timezone') - sudo "bash -c 'echo $CAPISTRANO:VAR$ > /etc/timezone'", opts - sudo "cp /usr/share/zoneinfo/$CAPISTRANO:VAR$ /etc/localtime", opts + rsudo "echo $CAPISTRANO:VAR$ > /etc/timezone", opts + rsudo "cp /usr/share/zoneinfo/$CAPISTRANO:VAR$ /etc/localtime", opts # restart syslog so that times match timezone - sudo "/etc/init.d/sysklogd restart" + sudo_script 'restart_syslog', <<-ENDSCRIPT + if [[ -x /etc/init.d/sysklogd ]]; then + /etc/init.d/sysklogd restart + elif [[ -x /etc/init.d/rsyslog ]]; then + service rsyslog restart + fi + ENDSCRIPT end + desc <<-DESC + Enable the ubuntu multiverse source for getting packages like + ec2-ami-tools used for bundling images + DESC + task :enable_multiverse do + sudo_script 'enable_multiverse', <<-ENDSCRIPT + if ! grep -qc multiverse /etc/apt/sources.list /etc/apt/sources.list.d/* &> /dev/null; then + cat /etc/apt/sources.list | sed 's/main universe/multiverse/' > /etc/apt/sources.list.d/rubber-multiverse-source.list + fi + ENDSCRIPT + end + def update_dyndns(instance_item) env = rubber_cfg.environment.bind(instance_item.role_names, instance_item.name) if env.dns_provider provider = Rubber::Dns::get_provider(env.dns_provider, env) provider.update(instance_item.name, instance_item.external_ip) @@ -241,29 +393,29 @@ end end expanded_pkg_list.join(' ') end - sudo "apt-get -q update" + rsudo "apt-get -q update" if upgrade - sudo "/bin/sh -c 'export DEBIAN_FRONTEND=noninteractive; apt-get -q -y --force-yes dist-upgrade'" + rsudo "export DEBIAN_FRONTEND=noninteractive; apt-get -q -y --force-yes dist-upgrade" else - sudo "/bin/sh -c 'export DEBIAN_FRONTEND=noninteractive; apt-get -q -y --force-yes install $CAPISTRANO:VAR$'", opts + rsudo "export DEBIAN_FRONTEND=noninteractive; apt-get -q -y --force-yes install $CAPISTRANO:VAR$", opts end end def custom_package(url_base, name, ver, install_test) - rubber.run_script "install_#{name}", <<-ENDSCRIPT + rubber.sudo_script "install_#{name}", <<-ENDSCRIPT if [[ #{install_test} ]]; then arch=`uname -m` if [ "$arch" = "x86_64" ]; then src="#{url_base}/#{name}_#{ver}_amd64.deb" else src="#{url_base}/#{name}_#{ver}_i386.deb" fi src_file="${src##*/}" - wget -qP /tmp ${src} + wget -qNP /tmp ${src} dpkg -i /tmp/${src_file} fi ENDSCRIPT end @@ -278,15 +430,61 @@ else logger.info data end end + # Rubygems always installs even if the gem is already installed + # When providing versions, rubygems fails unless versions are provided for all gems + # This helper script works around these issues by installing gems only if they + # aren't already installed, and separates versioned/unversioned into two separate + # calls to rubygems + # + set :gem_helper_script, <<-'ENDSCRIPT' + gem_cmd = ARGV[0] + gems = ARGV[1..-1] + cmd = "gem #{gem_cmd} --no-rdoc --no-ri" + + to_install = {} + to_install_ver = {} + # gem list passed in, possibly with versions, as "gem1 gem2:1.2 gem3" + gems.each do |gem_spec| + parts = gem_spec.split(':') + if parts[1] + to_install_ver[parts[0]] = parts[1] + else + to_install[parts[0]] = true + end + end + + installed = {} + `gem list --local`.each_line do |line| + parts = line.scan(/(.*) \((.*)\)/).first + next unless parts && parts.size == 2 + installed[parts[0]] = parts[1].split(",") + end + + to_install.delete_if {|g, v| installed.has_key?(g) } if gem_cmd == 'install' + to_install_ver.delete_if {|g, v| installed.has_key?(g) && installed[g].include?(v) } + + # rubygems can only do asingle versioned gem at a time so we need + # to do the two groups separately + # install versioned ones first so unversioned don't pull in a newer version + to_install_ver.each do |g, v| + system "#{cmd} #{g} -v #{v}" + fail "Unable to install versioned gem #{g}:#{v}" if $?.exitstatus > 0 + end + if to_install.size > 0 + gem_list = to_install.keys.join(' ') + system "#{cmd} #{gem_list}" + fail "Unable to install gems" if $?.exitstatus > 0 + end + ENDSCRIPT + # Helper for installing gems,allows one to respond to prompts def gem_helper(update=false) cmd = update ? "update" : "install" - opts = get_host_options('gems') do |gem_list| expanded_gem_list = [] gem_list.each do |gem_spec| if gem_spec.is_a?(Array) expanded_gem_list << "#{gem_spec[0]}:#{gem_spec[1]}" @@ -296,62 +494,13 @@ end expanded_gem_list.join(' ') end if opts.size > 0 - # Rubygems always installs even if the gem is already installed - # When providing versions, rubygems fails unless versions are provided for all gems - # This helper script works around these issues by installing gems only if they - # aren't already installed, and separates versioned/unversioned into two separate - # calls to rubygems - script = prepare_script 'gem_helper', <<-'ENDSCRIPT' - ruby - $@ <<-'EOF' - - gem_cmd = ARGV[0] - gems = ARGV[1..-1] - cmd = "gem #{gem_cmd} --no-rdoc --no-ri" - - to_install = {} - to_install_ver = {} - # gem list passed in, possibly with versions, as "gem1 gem2:1.2 gem3" - gems.each do |gem_spec| - parts = gem_spec.split(':') - if parts[1] - to_install_ver[parts[0]] = parts[1] - else - to_install[parts[0]] = true - end - end - - installed = {} - `gem list --local`.each do |line| - parts = line.scan(/(.*) \((.*)\)/).first - next unless parts && parts.size == 2 - installed[parts[0]] = parts[1].split(",") - end - - to_install.delete_if {|g, v| installed.has_key?(g) } if gem_cmd == 'install' - to_install_ver.delete_if {|g, v| installed.has_key?(g) && installed[g].include?(v) } - - # rubygems can only do asingle versioned gem at a time so we need - # to do the two groups separately - # install versioned ones first so unversioned don't pull in a newer version - to_install_ver.each do |g, v| - system "#{cmd} #{g} -v #{v}" - fail "Unable to install versioned gem #{g}:#{v}" if $?.exitstatus > 0 - end - if to_install.size > 0 - gem_list = to_install.keys.join(' ') - system "#{cmd} #{gem_list}" - fail "Unable to install gems" if $?.exitstatus > 0 - end - - 'EOF' - ENDSCRIPT - - sudo "sh #{script} #{cmd} $CAPISTRANO:VAR$", opts do |ch, str, data| + script = prepare_script('gem_helper', gem_helper_script, nil) + rsudo "ruby #{script} #{cmd} $CAPISTRANO:VAR$", opts do |ch, str, data| handle_gem_prompt(ch, data, str) end end end -end \ No newline at end of file +end