From e8de57c6309daeb5e25e1b0973adb8214255077f Mon Sep 17 00:00:00 2001 From: elijah Date: Mon, 27 Jun 2016 14:09:11 -0700 Subject: remove capistrano, switch to sshkit --- leap_cli.gemspec | 5 +- lib/leap_cli/bootstrap.rb | 11 ++ lib/leap_cli/log.rb | 9 +- lib/leap_cli/remote/leap_plugin.rb | 192 ------------------------------ lib/leap_cli/remote/puppet_plugin.rb | 26 ---- lib/leap_cli/remote/rsync_plugin.rb | 35 ------ lib/leap_cli/remote/tasks.rb | 51 -------- vendor/rsync_command/README.md | 12 +- vendor/rsync_command/lib/rsync_command.rb | 61 ++++++++-- 9 files changed, 78 insertions(+), 324 deletions(-) delete mode 100644 lib/leap_cli/remote/leap_plugin.rb delete mode 100644 lib/leap_cli/remote/puppet_plugin.rb delete mode 100644 lib/leap_cli/remote/rsync_plugin.rb delete mode 100644 lib/leap_cli/remote/tasks.rb diff --git a/leap_cli.gemspec b/leap_cli.gemspec index e54c88e..82c5294 100644 --- a/leap_cli.gemspec +++ b/leap_cli.gemspec @@ -50,10 +50,11 @@ spec = Gem::Specification.new do |s| # note: gli version is also pinned in leap_cli.rb. # network gems - s.add_runtime_dependency('net-ssh', '~> 2.7') + #s.add_runtime_dependency('net-ssh', '~> 2.7') # ^^ we can upgrade once we get off broken capistrano # https://github.com/net-ssh/net-ssh/issues/145 - s.add_runtime_dependency('capistrano', '~> 2.15') + #s.add_runtime_dependency('capistrano', '~> 2.15') + s.add_runtime_dependency('sshkit', '~> 1.11') # crypto gems # s.add_runtime_dependency('gpgme') # << does not build on debian jessie, so now optional. diff --git a/lib/leap_cli/bootstrap.rb b/lib/leap_cli/bootstrap.rb index 9ccb3dd..bc43115 100644 --- a/lib/leap_cli/bootstrap.rb +++ b/lib/leap_cli/bootstrap.rb @@ -39,6 +39,7 @@ module LeapCli if LeapCli.logger.log_level >= 2 log_version end + add_platform_lib_to_path load_commands(app) load_macros end @@ -193,5 +194,15 @@ module LeapCli end end + # + # makes all the ruby libraries in the leap_platform/lib directory + # available for inclusion. + # + def add_platform_lib_to_path + if Path.platform + path = File.join(Path.platform, 'lib') + $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path) + end + end end end diff --git a/lib/leap_cli/log.rb b/lib/leap_cli/log.rb index 3bd4f45..25f7b74 100644 --- a/lib/leap_cli/log.rb +++ b/lib/leap_cli/log.rb @@ -21,7 +21,7 @@ module LeapCli # thread safe logger def new_logger - LeapCli::LeapLogger.new + logger.dup #LeapCli::LeapLogger.new end # deprecated @@ -101,6 +101,9 @@ module LeapCli if options[:wrap] message = message.split("\n") end + if options[:color] && prefix.empty? + message = colorize(message, options[:color], options[:style]) + end else prefix = clear_prefix end @@ -117,6 +120,10 @@ module LeapCli end end + def debug(*args) + self.log(3, *args) + end + # # Add a raw log entry, without any modifications (other than indent). # Content to be logged is yielded by the block. diff --git a/lib/leap_cli/remote/leap_plugin.rb b/lib/leap_cli/remote/leap_plugin.rb deleted file mode 100644 index e6305ae..0000000 --- a/lib/leap_cli/remote/leap_plugin.rb +++ /dev/null @@ -1,192 +0,0 @@ -# -# these methods are made available in capistrano tasks as 'leap.method_name' -# (see RemoteCommand::new_capistrano) -# - -module LeapCli; module Remote; module LeapPlugin - - def required_packages - "puppet rsync lsb-release locales" - end - - def log(*args, &block) - LeapCli.logger.log(*args, &block) - end - - # - # creates directories that are owned by root and 700 permissions - # - def mkdirs(*dirs) - raise ArgumentError.new('illegal dir name') if dirs.grep(/[\' ]/).any? - run dirs.collect{|dir| "mkdir -m 700 -p #{dir}; "}.join - end - - # - # echos "ok" if the node has been initialized and the required packages are installed, bails out otherwise. - # - def assert_initialized - begin - test_initialized_file = "test -f #{Leap::Platform.init_path}" - check_required_packages = "! dpkg-query -W --showformat='${Status}\n' #{required_packages} 2>&1 | grep -q -E '(deinstall|no packages)'" - run "#{test_initialized_file} && #{check_required_packages} && echo ok" - rescue Capistrano::CommandError => exc - LeapCli::Util.bail! do - exc.hosts.each do |host| - node = host.to_s.split('.').first - LeapCli::Util.log :error, "running deploy: node not initialized. Run 'leap node init #{node}'", :host => host - end - end - end - end - - # - # bails out the deploy if the file /etc/leap/no-deploy exists. - # This kind of sucks, because it would be better to skip over nodes that have no-deploy set instead - # halting the entire deploy. As far as I know, with capistrano, there is no way to close one of the - # ssh connections in the pool and make sure it gets no further commands. - # - def check_for_no_deploy - begin - run "test ! -f /etc/leap/no-deploy" - rescue Capistrano::CommandError => exc - LeapCli::Util.bail! do - exc.hosts.each do |host| - LeapCli::Util.log "Can't continue because file /etc/leap/no-deploy exists", :host => host - end - end - end - end - - # - # dumps debugging information - # # - def debug - run "#{Leap::Platform.leap_dir}/bin/debug.sh" - end - - # - # dumps the recent deploy history to the console - # - def history(lines) - command = "(test -s /var/log/leap/deploy-summary.log && tail -n #{lines} /var/log/leap/deploy-summary.log) || (test -s /var/log/leap/deploy-summary.log.1 && tail -n #{lines} /var/log/leap/deploy-summary.log.1) || (echo 'no history')" - run command - end - - # - # This is a hairy ugly hack, exactly the kind of stuff that makes ruby - # dangerous and too much fun for its own good. - # - # In most places, we run remote ssh without a current 'task'. This works fine, - # except that in a few places, the behavior of capistrano ssh is controlled by - # the options of the current task. - # - # We don't want to create an actual current task, because tasks are no fun - # and can't take arguments or return values. So, when we need to configure - # things that can only be configured in a task, we use this handy hack to - # fake the current task. - # - # This is NOT thread safe, but could be made to be so with some extra work. - # - def with_task(name) - task = @config.tasks[name] - @config.class.send(:alias_method, :original_current_task, :current_task) - @config.class.send(:define_method, :current_task, Proc.new(){ task }) - begin - yield - ensure - @config.class.send(:remove_method, :current_task) - @config.class.send(:alias_method, :current_task, :original_current_task) - end - end - - # - # similar to run(cmd, &block), but with: - # - # * exit codes - # * stdout and stderr are combined - # - def stream(cmd, &block) - command = '%s 2>&1; echo "exitcode=$?"' % cmd - run(command) do |channel, stream, data| - exitcode = nil - if data =~ /exitcode=(\d+)\n/ - exitcode = $1.to_i - data.sub!(/exitcode=(\d+)\n/,'') - end - yield({:host => channel[:host], :data => data, :exitcode => exitcode}) - end - end - - # - # like stream, but capture all the output before returning - # - def capture(cmd, &block) - command = '%s 2>&1; echo "exitcode=$?" 2>&1;' % cmd - host_data = {} - run(command) do |channel, stream, data| - host_data[channel[:host]] ||= "" - if data =~ /exitcode=(\d+)\n/ - exitcode = $1.to_i - data.sub!(/exitcode=(\d+)\n/,'') - host_data[channel[:host]] += data - yield({:host => channel[:host], :data => host_data[channel[:host]], :exitcode => exitcode}) - else - host_data[channel[:host]] += data - end - end - end - - # - # Run a command, with a nice status report and progress indicator. - # Only successful results are returned, errors are printed. - # - # For each successful run on each host, block is yielded with a hash like so: - # - # {:host => 'bluejay', :exitcode => 0, :data => 'shell output'} - # - def run_with_progress(cmd, &block) - ssh_failures = [] - exitcode_failures = [] - succeeded = [] - task = LeapCli.logger.log_level > 1 ? :standard_task : :skip_errors_task - with_task(task) do - log :querying, 'facts' do - progress " " - call_on_failure do |host| - ssh_failures << host - progress 'F' - end - capture(cmd) do |response| - if response[:exitcode] == 0 - progress '.' - yield response - else - exitcode_failures << response - progress 'F' - end - end - end - end - puts "done" - if ssh_failures.any? - log :failed, 'to connect to nodes: ' + ssh_failures.join(' ') - end - if exitcode_failures.any? - log :failed, 'to run successfully:' do - exitcode_failures.each do |response| - log "[%s] exit %s - %s" % [response[:host], response[:exitcode], response[:data].strip] - end - end - end - rescue Capistrano::RemoteError => err - log :error, err.to_s - end - - private - - def progress(str='.') - print str - STDOUT.flush - end - -end; end; end diff --git a/lib/leap_cli/remote/puppet_plugin.rb b/lib/leap_cli/remote/puppet_plugin.rb deleted file mode 100644 index 5a6e908..0000000 --- a/lib/leap_cli/remote/puppet_plugin.rb +++ /dev/null @@ -1,26 +0,0 @@ -# -# these methods are made available in capistrano tasks as 'puppet.method_name' -# (see RemoteCommand::new_capistrano) -# - -module LeapCli; module Remote; module PuppetPlugin - - def apply(options) - run "#{Leap::Platform.leap_dir}/bin/puppet_command set_hostname apply #{flagize(options)}" - end - - private - - def flagize(hsh) - hsh.inject([]) {|str, item| - if item[1] === false - str - elsif item[1] === true - str << "--" + item[0].to_s - else - str << "--" + item[0].to_s + " " + item[1].inspect - end - }.join(' ') - end - -end; end; end diff --git a/lib/leap_cli/remote/rsync_plugin.rb b/lib/leap_cli/remote/rsync_plugin.rb deleted file mode 100644 index a6708f4..0000000 --- a/lib/leap_cli/remote/rsync_plugin.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -# these methods are made available in capistrano tasks as 'rsync.method_name' -# (see RemoteCommand::new_capistrano) -# - -autoload :RsyncCommand, 'rsync_command' - -module LeapCli; module Remote; module RsyncPlugin - - # - # takes a block, yielded a server, that should return a hash with various rsync options. - # supported options include: - # - # {:source => '', :dest => '', :flags => '', :includes => [], :excludes => []} - # - def update - rsync = RsyncCommand.new(:logger => logger) - rsync.asynchronously(find_servers) do |server| - options = yield server - next unless options - remote_user = server.user || fetch(:user, ENV['USER']) - src = options[:source] - dest = {:user => remote_user, :host => server.host, :path => options[:dest]} - options[:ssh] = ssh_options.merge(server.options[:ssh_options]||{}) - options[:chdir] ||= Path.provider - rsync.exec(src, dest, options) - end - if rsync.failed? - LeapCli::Util.bail! do - LeapCli::Util.log :failed, "to rsync to #{rsync.failures.map{|f|f[:dest][:host]}.join(' ')}" - end - end - end - -end; end; end diff --git a/lib/leap_cli/remote/tasks.rb b/lib/leap_cli/remote/tasks.rb deleted file mode 100644 index d08d19a..0000000 --- a/lib/leap_cli/remote/tasks.rb +++ /dev/null @@ -1,51 +0,0 @@ -# -# This file is evaluated just the same as a typical capistrano "deploy.rb" -# For DSL manual, see https://github.com/capistrano/capistrano/wiki -# - -MAX_HOSTS = 10 - -task :install_authorized_keys, :max_hosts => MAX_HOSTS do - leap.log :updating, "authorized_keys" do - leap.mkdirs '/root/.ssh' - upload LeapCli::Path.named_path(:authorized_keys), '/root/.ssh/authorized_keys', :mode => '600' - end -end - -# -# for vagrant nodes, we install insecure vagrant key to authorized_keys2, since deploy -# will overwrite authorized_keys. -# -# why force the insecure vagrant key? -# if we don't do this, then first time initialization might fail if the user has many keys -# (ssh will bomb out before it gets to the vagrant key). -# and it really doesn't make sense to ask users to pin the insecure vagrant key in their -# .ssh/config files. -# -task :install_insecure_vagrant_key, :max_hosts => MAX_HOSTS do - leap.log :installing, "insecure vagrant key" do - leap.mkdirs '/root/.ssh' - upload LeapCli::Path.vagrant_ssh_pub_key_file, '/root/.ssh/authorized_keys2', :mode => '600' - end -end - -task :install_prerequisites, :max_hosts => MAX_HOSTS do - bin_dir = File.join(Leap::Platform.leap_dir, 'bin') - node_init_path = File.join(bin_dir, 'node_init') - - leap.log :running, "node_init script" do - leap.mkdirs bin_dir - upload LeapCli::Path.node_init_script, node_init_path, :mode => '500' - run node_init_path - end -end - -# -# just dummies, used to capture task options -# - -task :skip_errors_task, :on_error => :continue, :max_hosts => MAX_HOSTS do -end - -task :standard_task, :max_hosts => MAX_HOSTS do -end diff --git a/vendor/rsync_command/README.md b/vendor/rsync_command/README.md index 4b53a5c..5e44845 100644 --- a/vendor/rsync_command/README.md +++ b/vendor/rsync_command/README.md @@ -11,13 +11,15 @@ Installation Usage ------------------------------------ - rsync = RsyncCommand.new(:logger => logger, :ssh => {:auth_methods => 'publickey'}, :flags => '-a') - source = '/source/path' + rsync = RsyncCommand.new(:ssh => {:auth_methods => 'publickey'}, :flags => '-a') servers = ['red', 'green', 'blue'] - rsync.asynchronously(servers) do |server| - dest = {:user => 'root', :host => server, :path => '/dest/path'} - rsync.exec(source, dest) + rsync.asynchronously(servers) do |sync, server| + sync.user = 'root' + sync.host = server + sync.source = '/from' + sync.dest = '/to' + sync.exec end if rsync.failed? diff --git a/vendor/rsync_command/lib/rsync_command.rb b/vendor/rsync_command/lib/rsync_command.rb index 39e5945..bdcafe0 100644 --- a/vendor/rsync_command/lib/rsync_command.rb +++ b/vendor/rsync_command/lib/rsync_command.rb @@ -4,6 +4,44 @@ require "rsync_command/thread_pool" require 'monitor' +class RsyncRunner + attr_accessor :logger + attr_accessor :source, :dest, :flags, :includes, :excludes + attr_accessor :user, :host + attr_accessor :chdir, :ssh + def initialize(rsync_command) + @logger = nil + @source = "" + @dest = "" + @flags = "" + @includes = [] + @excludes = [] + @rsync_command = rsync_command + end + def log(*args) + @logger.log(*args) + end + def valid? + !@source.empty? || !@dest.empty? + end + def to_hash + fields = [:flags, :includes, :excludes, :logger, :ssh, :chdir] + fields.inject({}){|hsh, i| + hsh[i] = self.send(i); hsh + } + end + def exec + return unless valid? + dest = { + :user => self.user, + :host => self.host, + :path => self.dest + } + src = self.source + @rsync_command.exec_rsync(src, dest, self.to_hash) + end +end + class RsyncCommand attr_accessor :failures, :logger @@ -21,15 +59,23 @@ class RsyncCommand def asynchronously(array, &block) pool = ThreadPool.new array.each do |item| - pool.schedule(item, &block) + pool.schedule(RsyncRunner.new(self), item, &block) end pool.shutdown end + # + # returns true if last exec returned a failure + # + def failed? + @failures && @failures.any? + end + # # runs rsync, recording failures # - def exec(src, dest, options={}) + def exec_rsync(src, dest, options={}) + logger = options[:logger] || @logger @failures.synchronize do @failures.clear end @@ -37,7 +83,7 @@ class RsyncCommand if options[:chdir] rsync_cmd = "cd '#{options[:chdir]}'; #{rsync_cmd}" end - @logger.debug rsync_cmd if @logger + logger.debug rsync_cmd if logger ok = system(rsync_cmd) unless ok @failures.synchronize do @@ -46,13 +92,6 @@ class RsyncCommand end end - # - # returns true if last exec returned a failure - # - def failed? - @failures && @failures.any? - end - # # build rsync command # @@ -70,8 +109,6 @@ class RsyncCommand "rsync #{flags.compact.join(' ')} #{src} #{dest}" end - private - # # Creates an rsync location if the +address+ is a hash with keys :user, :host, and :path # (each component is optional). If +address+ is a string, we just pass it through. -- cgit v1.2.3