summaryrefslogtreecommitdiff
path: root/lib/leap_cli/commands
diff options
context:
space:
mode:
authorMicah Anderson <micah@riseup.net>2017-11-28 11:35:01 -0500
committerMicah Anderson <micah@riseup.net>2017-11-28 11:35:01 -0500
commit0d251e2ceddd3e02ed8bba8725830689dbdd1397 (patch)
tree37d7096d9e458ca1e6431dff8a2f571553011c44 /lib/leap_cli/commands
parent93a181d44e2d8163ae44945aac1b6477e268170d (diff)
parentbf6c56d86c7ba45e7ca766d990a9e9162025e5ac (diff)
Merge tag 'refs/tags/0.10.0' into stable
Release 0.10.0
Diffstat (limited to 'lib/leap_cli/commands')
-rw-r--r--lib/leap_cli/commands/cert.rb (renamed from lib/leap_cli/commands/ca.rb)68
-rw-r--r--lib/leap_cli/commands/compile.rb2
-rw-r--r--lib/leap_cli/commands/node.rb27
-rw-r--r--lib/leap_cli/commands/ping.rb58
-rw-r--r--lib/leap_cli/commands/run.rb53
-rw-r--r--lib/leap_cli/commands/test.rb2
-rw-r--r--lib/leap_cli/commands/user.rb37
-rw-r--r--lib/leap_cli/commands/vagrant.rb4
-rw-r--r--lib/leap_cli/commands/vm.rb5
9 files changed, 217 insertions, 39 deletions
diff --git a/lib/leap_cli/commands/ca.rb b/lib/leap_cli/commands/cert.rb
index 3c5fc7d5..68fa9444 100644
--- a/lib/leap_cli/commands/ca.rb
+++ b/lib/leap_cli/commands/cert.rb
@@ -102,13 +102,13 @@ module LeapCli; module Commands
def generate_test_client_cert(prefix=nil)
require 'leap_cli/x509'
cert = CertificateAuthority::Certificate.new
- cert.serial_number.number = cert_serial_number(provider.domain)
- cert.subject.common_name = [prefix, random_common_name(provider.domain)].join
+ cert.serial_number.number = X509.cert_serial_number(provider.domain)
+ cert.subject.common_name = [prefix, X509.random_common_name(provider.domain)].join
cert.not_before = X509.yesterday
cert.not_after = X509.yesterday.advance(:years => 1)
cert.key_material.generate_key(1024) # just for testing, remember!
- cert.parent = client_ca_root
- cert.sign! client_test_signing_profile
+ cert.parent = X509.client_ca_root
+ cert.sign! X509.client_test_signing_profile
yield cert.key_material.private_key.to_pem, cert.to_pem
end
@@ -281,11 +281,13 @@ module LeapCli; module Commands
if status == 'valid'
log 'authorized!', color: :green, style: :bold
elsif status == 'error'
- bail! :error, message
+ bail! :error, message.inspect
elsif status == 'unauthorized'
- bail!(:unauthorized, message, color: :yellow, style: :bold) do
+ bail!(:unauthorized, message.inspect, color: :yellow, style: :bold) do
log 'You must first run `leap cert register` to register the account key with letsencrypt.org'
end
+ else
+ bail!(:error, "unrecognized status: #{status.inspect}, #{message.inspect}")
end
log :fetching, "new certificate from letsencrypt.org"
@@ -335,31 +337,41 @@ module LeapCli; module Commands
# This method will bail if any checks fail.
#
def domain_ready_for_acme!(domain)
- begin
- uri = URI("https://#{domain}/.well-known/acme-challenge/ok")
- options = {
- use_ssl: true,
- open_timeout: 5,
- verify_mode: OpenSSL::SSL::VERIFY_NONE
- }
- Net::HTTP.start(uri.host, uri.port, options) do |http|
- http.request(Net::HTTP::Get.new(uri)) do |response|
- if !response.is_a?(Net::HTTPSuccess)
- bail!(:error, "Could not GET %s" % uri) do
- log "%s %s" % [response.code, response.message]
- log "You may need to run `leap deploy`"
- end
+ uri = URI("https://#{domain}/.well-known/acme-challenge/ok")
+ options = {
+ use_ssl: true,
+ open_timeout: 5,
+ verify_mode: OpenSSL::SSL::VERIFY_NONE
+ }
+ http_get(uri, options)
+ end
+
+ private
+
+ def http_get(uri, options, limit = 10)
+ raise ArgumentError, "HTTP redirect too deep (#{uri})" if limit == 0
+ Net::HTTP.start(uri.host, uri.port, options) do |http|
+ http.request(Net::HTTP::Get.new(uri)) do |response|
+ case response
+ when Net::HTTPSuccess then
+ return response
+ when Net::HTTPRedirection then
+ return http_get(URI(response['location']), options, limit - 1)
+ else
+ bail!(:error, "Could not GET %s" % uri) do
+ log "%s %s" % [response.code, response.message]
+ log "You may need to run `leap deploy`"
end
end
end
- rescue Errno::ETIMEDOUT, Net::OpenTimeout
- bail! :error, "Connection attempt timed out: %s" % uri
- rescue Interrupt
- bail!
- rescue StandardError => exc
- bail!(:error, "Could not GET %s" % uri) do
- log exc.to_s
- end
+ end
+ rescue Errno::ETIMEDOUT, Net::OpenTimeout
+ bail! :error, "Connection attempt timed out: %s" % uri
+ rescue Interrupt
+ bail!
+ rescue StandardError => exc
+ bail!(:error, "Could not GET %s" % uri) do
+ log exc.to_s
end
end
diff --git a/lib/leap_cli/commands/compile.rb b/lib/leap_cli/commands/compile.rb
index 92c879d7..16dff3df 100644
--- a/lib/leap_cli/commands/compile.rb
+++ b/lib/leap_cli/commands/compile.rb
@@ -155,7 +155,7 @@ module LeapCli
buffer = StringIO.new
keys = Dir.glob(path([:user_ssh, '*']))
if keys.empty?
- bail! "You must have at least one public SSH user key configured in order to proceed. See `leap help add-user`."
+ bail! "You must have at least one public SSH user key configured in order to proceed. See `leap help user add`."
end
if file_exists?(path(:monitor_pub_key))
keys << path(:monitor_pub_key)
diff --git a/lib/leap_cli/commands/node.rb b/lib/leap_cli/commands/node.rb
index 60540de9..9cde15bc 100644
--- a/lib/leap_cli/commands/node.rb
+++ b/lib/leap_cli/commands/node.rb
@@ -45,6 +45,23 @@ module LeapCli; module Commands
do_node_rm(global_options, options, args)
end
end
+
+ node.desc 'Mark a node as disabled.'
+ node.arg_name 'NAME'
+ node.command :disable do |cmd|
+ cmd.action do |global_options,options,args|
+ do_node_disable(global_options, options, args)
+ end
+ end
+
+ node.desc 'Mark a node as enabled.'
+ node.arg_name 'NAME'
+ node.command :enable do |cmd|
+ cmd.action do |global_options,options,args|
+ do_node_enable(global_options, options, args)
+ end
+ end
+
end
##
@@ -126,4 +143,14 @@ module LeapCli; module Commands
remove_node_facts(node.name)
end
+ def do_node_enable(global, options, args)
+ node = get_node_from_args(args, include_disabled: true)
+ node.update_json({}, remove: ["enabled"])
+ end
+
+ def do_node_disable(global, options, args)
+ node = get_node_from_args(args, include_disabled: true)
+ node.update_json("enabled" => false)
+ end
+
end; end
diff --git a/lib/leap_cli/commands/ping.rb b/lib/leap_cli/commands/ping.rb
new file mode 100644
index 00000000..4283d9b3
--- /dev/null
+++ b/lib/leap_cli/commands/ping.rb
@@ -0,0 +1,58 @@
+module LeapCli; module Commands
+
+ desc "Ping nodes to see if they are alive."
+ long_desc "Attempts to ping each node in the FILTER set."
+ arg_name "FILTER"
+ command :ping do |c|
+ c.flag 'timeout', :arg_name => "TIMEOUT",
+ :default_value => 2, :desc => 'Wait at most TIMEOUT seconds.'
+ c.flag 'count', :arg_name => "COUNT",
+ :default_value => 2, :desc => 'Ping COUNT times.'
+ c.action do |global, options, args|
+ do_ping(global, options, args)
+ end
+ end
+
+ private
+
+ def do_ping(global, options, args)
+ assert_bin!('ping')
+
+ timeout = [options[:timeout].to_i, 1].max
+ count = [options[:count].to_i, 1].max
+ nodes = nil
+
+ if args && args.any?
+ node = manager.disabled_node(args.first)
+ if node
+ nodes = Config::ObjectList.new
+ nodes.add(node.name, node)
+ end
+ end
+
+ nodes ||= manager.filter! args
+
+ threads = []
+ nodes.each_node do |node|
+ threads << Thread.new do
+ cmd = "ping -i 0.2 -n -q -W #{timeout} -c #{count} #{node.ip_address} 2>&1"
+ log(2, cmd)
+ output = `#{cmd}`
+ if $?.success?
+ last = output.split("\n").last
+ times = last.split('=').last.strip
+ min, avg, max, mdev = times.split('/')
+ log("ping #{min} ms", host: node.name, color: :green)
+ else
+ log(:failed, "to ping #{node.ip_address}", host: node.name)
+ end
+ end
+ end
+ threads.map(&:join)
+
+ log("done")
+ end
+
+end; end
+
+
diff --git a/lib/leap_cli/commands/run.rb b/lib/leap_cli/commands/run.rb
index cad9b7a0..9149d594 100644
--- a/lib/leap_cli/commands/run.rb
+++ b/lib/leap_cli/commands/run.rb
@@ -3,13 +3,27 @@ module LeapCli; module Commands
desc 'Run a shell command remotely'
long_desc "Runs the specified command COMMAND on each node in the FILTER set. " +
"For example, `leap run 'uname -a' webapp`"
- arg_name 'COMMAND FILTER'
command :run do |c|
c.switch 'stream', :default => false, :desc => 'If set, stream the output as it arrives. (default: --stream for a single node, --no-stream for multiple nodes)'
c.flag 'port', :arg_name => 'SSH_PORT', :desc => 'Override default SSH port used when trying to connect to the server.'
- c.action do |global, options, args|
- run_shell_command(global, options, args)
+
+ c.desc 'Run an arbitrary shell command.'
+ c.arg_name 'FILTER', optional: true
+ c.command :command do |command|
+ command.action do |global, options, args|
+ run_shell_command(global, options, args)
+ end
+ end
+
+ c.desc 'Generate one or more new invite codes.'
+ c.arg_name '[COUNT] [ENVIRONMENT]'
+ c.command :invite do |invite|
+ invite.action do |global_options,options,args|
+ run_new_invites(global_options, options, args)
+ end
end
+
+ c.default_command :command
end
private
@@ -27,6 +41,39 @@ module LeapCli; module Commands
end
end
+ CMD_NEW_INVITES="cd /srv/leap/webapp; RAILS_ENV=production bundle exec rake \"generate_invites[NUM,USES]\""
+
+ def run_new_invites(global, options, args)
+ require 'leap_cli/ssh'
+ count = 1
+ uses = 1
+ env = nil
+ arg1 = args.shift
+ arg2 = args.shift
+ if arg1 && arg2
+ env = manager.env(arg2)
+ count = arg1
+ elsif arg1
+ env = manager.env(arg1)
+ else
+ env = manager.env(nil)
+ end
+ unless env
+ bail! "Environment name you specified does not match one that is available. See `leap env ls` for the available names"
+ end
+
+ env_name = env.name == 'default' ? nil : env.name
+ webapp_nodes = env.nodes[:environment => env_name][:services => 'webapp'].first
+ if webapp_nodes.empty?
+ bail! "Could not find a webapp node for the specified environment"
+ end
+ stream_command(
+ webapp_nodes,
+ CMD_NEW_INVITES.sub('NUM', count.to_s).sub('USES', uses.to_s),
+ options
+ )
+ end
+
def capture_command(nodes, cmd, options)
SSH.remote_command(nodes, options) do |ssh, host|
output = ssh.capture(cmd, :log_output => false)
diff --git a/lib/leap_cli/commands/test.rb b/lib/leap_cli/commands/test.rb
index 70eb00fd..e2815aae 100644
--- a/lib/leap_cli/commands/test.rb
+++ b/lib/leap_cli/commands/test.rb
@@ -35,7 +35,7 @@ module LeapCli; module Commands
SSH::remote_command(node, options) do |ssh, host|
ssh.stream(test_cmd(options), :raise_error => true, :log_wrap => true)
end
- rescue LeapCli::SSH::ExecuteError
+ rescue LeapCli::SSH::TimeoutError, SSHKit::Runner::ExecuteError, SSHKit::Command::Failed
if options[:continue]
exit_status(1)
else
diff --git a/lib/leap_cli/commands/user.rb b/lib/leap_cli/commands/user.rb
index 1ca92719..7fd5f52d 100644
--- a/lib/leap_cli/commands/user.rb
+++ b/lib/leap_cli/commands/user.rb
@@ -113,14 +113,42 @@ module LeapCli
def do_list_users(global, options, args)
require 'leap_cli/ssh'
+ ssh_keys = {}
+ Dir.glob("#{ENV['HOME']}/.ssh/*.pub").each do |keyfile|
+ key = SSH::Key.load(keyfile)
+ ssh_keys[key.fingerprint] = key if key
+ end
+
+ ssh_agent_keys = {}
+ if !`which ssh-add`.empty?
+ `ssh-add -L`.split("\n").each do |keystring|
+ key = SSH::Key.load(keystring)
+ ssh_agent_keys[key.fingerprint] = key if key
+ end
+ end
+
Dir.glob(path([:user_ssh, '*'])).each do |keyfile|
username = File.basename(File.dirname(keyfile))
log username, :color => :cyan do
log Path.relative_path(keyfile)
key = SSH::Key.load(keyfile)
- log 'SSH MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :ssh, :encoding => :hex)
- log 'SSH SHA256 fingerprint: ' + key.fingerprint(:digest => :sha256, :type => :ssh, :encoding => :base64)
- log 'DER MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :der, :encoding => :hex)
+ if key.nil?
+ log :warning, "could not read ssh key #{keyfile}" do
+ log "currently, only these ssh key types are supported: " + SSH::Key::SUPPORTED_TYPES.join(", ")
+ end
+ else
+ log 'SSH MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :ssh, :encoding => :hex)
+ log 'SSH SHA256 fingerprint: ' + key.fingerprint(:digest => :sha256, :type => :ssh, :encoding => :base64)
+ log 'DER MD5 fingerprint: ' + key.fingerprint(:digest => :md5, :type => :der, :encoding => :hex)
+ if ssh_keys[key.fingerprint]
+ log 'Matches local key: ' + ssh_keys[key.fingerprint].filename, color: :green
+ if ssh_agent_keys[key.fingerprint]
+ log 'Matches ssh-agent key: ' + ssh_agent_keys[key.fingerprint].summary(encoding: :base64), color: :green
+ else
+ log :error, 'No matching key in the ssh-agent'
+ end
+ end
+ end
end
end
end
@@ -154,6 +182,9 @@ module LeapCli
end
else
key_index = 0
+ log "Picking the only compatible ssh key: "+ ssh_keys[key_index].filename do
+ log ssh_keys[key_index].summary
+ end
end
return ssh_keys[key_index]
diff --git a/lib/leap_cli/commands/vagrant.rb b/lib/leap_cli/commands/vagrant.rb
index f8a75b61..78b2fede 100644
--- a/lib/leap_cli/commands/vagrant.rb
+++ b/lib/leap_cli/commands/vagrant.rb
@@ -132,10 +132,10 @@ module LeapCli; module Commands
lines << %[ config.vm.provider "virtualbox" do |v|]
lines << %[ v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]]
lines << %[ v.name = "#{node.name}"]
- lines << %[ v.memory = 1536]
+ lines << %[ v.memory = 2048]
lines << %[ end]
lines << %[ config.vm.provider "libvirt" do |v|]
- lines << %[ v.memory = 1536]
+ lines << %[ v.memory = 2048]
lines << %[ end]
lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line
lines << %[ end]
diff --git a/lib/leap_cli/commands/vm.rb b/lib/leap_cli/commands/vm.rb
index 790774f1..6f97dbce 100644
--- a/lib/leap_cli/commands/vm.rb
+++ b/lib/leap_cli/commands/vm.rb
@@ -415,7 +415,6 @@ module LeapCli; module Commands
config = manager.env.cloud
name = nil
if options[:mock]
- Fog.mock!
name = 'mock_aws'
config['mock_aws'] = {
"api" => "aws",
@@ -451,6 +450,10 @@ module LeapCli; module Commands
assert! entry['api'] == 'aws', "cloud.json: currently, only 'aws' is supported for `api`."
assert! entry['vendor'] == 'aws', "cloud.json: currently, only 'aws' is supported for `vendor`."
+ LeapCli::Cloud::check_dependencies!(entry)
+ if options[:mock]
+ Fog.mock!
+ end
return LeapCli::Cloud.new(name, entry, node)
end