summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2016-06-27 14:09:11 -0700
committerelijah <elijah@riseup.net>2016-06-27 16:58:04 -0700
commite8de57c6309daeb5e25e1b0973adb8214255077f (patch)
tree101edba8252037bb5fc2ebbe197ec734a132495c
parente46c4c64128621c2e84233e27b63040c027be88f (diff)
remove capistrano, switch to sshkit
-rw-r--r--leap_cli.gemspec5
-rw-r--r--lib/leap_cli/bootstrap.rb11
-rw-r--r--lib/leap_cli/log.rb9
-rw-r--r--lib/leap_cli/remote/leap_plugin.rb192
-rw-r--r--lib/leap_cli/remote/puppet_plugin.rb26
-rw-r--r--lib/leap_cli/remote/rsync_plugin.rb35
-rw-r--r--lib/leap_cli/remote/tasks.rb51
-rw-r--r--vendor/rsync_command/README.md12
-rw-r--r--vendor/rsync_command/lib/rsync_command.rb61
9 files changed, 78 insertions, 324 deletions
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
@@ -47,13 +93,6 @@ class RsyncCommand
end
#
- # returns true if last exec returned a failure
- #
- def failed?
- @failures && @failures.any?
- end
-
- #
# build rsync command
#
def command(src, dest, options={})
@@ -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.