aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2012-11-28 14:08:39 -0800
committerelijah <elijah@riseup.net>2012-11-28 14:08:39 -0800
commite2c31618b6f70d86c55c348436dd600b2e4ace21 (patch)
tree2bf27e98fc62af402499c0e7736b02b280dfc320
parent16f9ee1668a06d6b83dfc312d0601d4f235ab8ef (diff)
downloadleap_cli-e2c31618b6f70d86c55c348436dd600b2e4ace21.tar.gz
leap_cli-e2c31618b6f70d86c55c348436dd600b2e4ace21.tar.bz2
command name shuffle -- grouped more commands together as subcommands
-rwxr-xr-xbin/leap1
-rw-r--r--lib/core_ext/string.rb14
-rw-r--r--lib/leap_cli.rb20
-rw-r--r--lib/leap_cli/commands/ca.rb255
-rw-r--r--lib/leap_cli/commands/compile.rb2
-rw-r--r--lib/leap_cli/commands/node.rb58
-rw-r--r--lib/leap_cli/commands/test.rb25
-rw-r--r--lib/leap_cli/remote/plugin.rb2
-rw-r--r--lib/lib_ext/gli.rb56
-rw-r--r--test/leap_platform/provider_base/services/openvpn.json2
10 files changed, 253 insertions, 182 deletions
diff --git a/bin/leap b/bin/leap
index 461ff3c..690a560 100755
--- a/bin/leap
+++ b/bin/leap
@@ -26,6 +26,7 @@ end
require 'gli'
require 'highline'
require 'forwardable'
+require 'lib_ext/gli' # our custom extensions to gli
#
# Typically, GLI and Highline methods are loaded into the global namespace.
diff --git a/lib/core_ext/string.rb b/lib/core_ext/string.rb
new file mode 100644
index 0000000..07af8e5
--- /dev/null
+++ b/lib/core_ext/string.rb
@@ -0,0 +1,14 @@
+#
+# make ruby 1.9 act more like ruby 1.8
+#
+unless String.method_defined?(:to_a)
+ class String
+ def to_a; [self]; end
+ end
+end
+
+unless String.method_defined?(:any?)
+ class String
+ def any?; self.chars.any?; end
+ end
+end
diff --git a/lib/leap_cli.rb b/lib/leap_cli.rb
index 74c5832..3f35cd4 100644
--- a/lib/leap_cli.rb
+++ b/lib/leap_cli.rb
@@ -5,6 +5,7 @@ require 'leap_cli/requirements.rb'
require 'core_ext/hash'
require 'core_ext/boolean'
require 'core_ext/nil'
+require 'core_ext/string'
require 'leap_cli/log'
require 'leap_cli/init'
@@ -22,25 +23,12 @@ require 'leap_cli/config/manager'
module LeapCli::Commands; end
+#
+# allow everyone easy access to log() command.
+#
module LeapCli
Util.send(:extend, LeapCli::Log)
Commands.send(:extend, LeapCli::Log)
Config::Manager.send(:include, LeapCli::Log)
extend LeapCli::Log
end
-
-#
-# make ruby 1.9 act more like ruby 1.8
-#
-unless String.method_defined?(:to_a)
- class String
- def to_a; [self]; end
- end
-end
-
-unless String.method_defined?(:any?)
- class String
- def any?; self.chars.any?; end
- end
-end
-
diff --git a/lib/leap_cli/commands/ca.rb b/lib/leap_cli/commands/ca.rb
index 05bdb2b..f471b5a 100644
--- a/lib/leap_cli/commands/ca.rb
+++ b/lib/leap_cli/commands/ca.rb
@@ -5,150 +5,155 @@ require 'digest/md5'
module LeapCli; module Commands
- desc 'Creates the public and private key for your Certificate Authority.'
- command :'init-ca' do |c|
- c.action do |global_options,options,args|
- assert_files_missing! :ca_cert, :ca_key
- assert_config! 'provider.ca.name'
- assert_config! 'provider.ca.bit_size'
- assert_config! 'provider.ca.life_span'
-
- provider = manager.provider
- root = CertificateAuthority::Certificate.new
-
- # set subject
- root.subject.common_name = provider.ca.name
- possible = ['country', 'state', 'locality', 'organization', 'organizational_unit', 'email_address']
- provider.ca.keys.each do |key|
- if possible.include?(key)
- root.subject.send(key + '=', provider.ca[key])
+ desc "Manage X.509 certificates"
+ #long_desc ""
+ command :cert do |c|
+
+ c.desc 'Creates a Certificate Authority (private key and CA certificate)'
+ c.command :ca do |c|
+ c.action do |global_options,options,args|
+ assert_files_missing! :ca_cert, :ca_key
+ assert_config! 'provider.ca.name'
+ assert_config! 'provider.ca.bit_size'
+ assert_config! 'provider.ca.life_span'
+
+ provider = manager.provider
+ root = CertificateAuthority::Certificate.new
+
+ # set subject
+ root.subject.common_name = provider.ca.name
+ possible = ['country', 'state', 'locality', 'organization', 'organizational_unit', 'email_address']
+ provider.ca.keys.each do |key|
+ if possible.include?(key)
+ root.subject.send(key + '=', provider.ca[key])
+ end
end
- end
- # set expiration
- root.not_before = today
- root.not_after = years_from_today(provider.ca.life_span.to_i)
+ # set expiration
+ root.not_before = today
+ root.not_after = years_from_today(provider.ca.life_span.to_i)
- # generate private key
- root.serial_number.number = 1
- root.key_material.generate_key(provider.ca.bit_size)
+ # generate private key
+ root.serial_number.number = 1
+ root.key_material.generate_key(provider.ca.bit_size)
- # sign self
- root.signing_entity = true
- root.parent = root
- root.sign!(ca_root_signing_profile)
+ # sign self
+ root.signing_entity = true
+ root.parent = root
+ root.sign!(ca_root_signing_profile)
- # save
- write_file!(:ca_key, root.key_material.private_key.to_pem)
- write_file!(:ca_cert, root.to_pem)
+ # save
+ write_file!(:ca_key, root.key_material.private_key.to_pem)
+ write_file!(:ca_cert, root.to_pem)
+ end
end
- end
- desc 'Creates or renews a X.509 certificate/key pair for a single node or all nodes'
- arg_name '<node-name | "all">', :optional => false, :multiple => false
- command :'update-cert' do |c|
- c.action do |global_options,options,args|
- assert_files_exist! :ca_cert, :ca_key, :msg => 'Run init-ca to create them'
- assert_config! 'provider.ca.server_certificates.bit_size'
- assert_config! 'provider.ca.server_certificates.digest'
- assert_config! 'provider.ca.server_certificates.life_span'
- assert_config! 'common.x509.use'
-
- if args.first == 'all' || args.empty?
- manager.each_node do |node|
- if cert_needs_updating?(node)
- generate_cert_for_node(node)
+ c.desc 'Creates or renews a X.509 certificate/key pair for a single node or all nodes'
+ c.arg_name 'node-name', :optional => false
+ c.command :update do |c|
+ c.action do |global_options,options,args|
+ assert_files_exist! :ca_cert, :ca_key, :msg => 'Run `leap cert ca` to create them'
+ assert_config! 'provider.ca.server_certificates.bit_size'
+ assert_config! 'provider.ca.server_certificates.digest'
+ assert_config! 'provider.ca.server_certificates.life_span'
+ assert_config! 'common.x509.use'
+
+ if args.first == 'all' || args.empty?
+ manager.each_node do |node|
+ if cert_needs_updating?(node)
+ generate_cert_for_node(node)
+ end
end
+ else
+ generate_cert_for_node(get_node_from_args(args))
end
- else
- generate_cert_for_node(get_node_from_args(args))
end
end
- end
- desc 'Generates Diffie-Hellman parameter file (needed for server-side of TLS connections)'
- command :'init-dh' do |c|
- c.action do |global_options,options,args|
- long_running do
- if cmd_exists?('certtool')
- log 0, 'Generating DH parameters (takes a long time)...'
- output = assert_run!('certtool --generate-dh-params --sec-param high')
- output.sub! /.*(-----BEGIN DH PARAMETERS-----.*-----END DH PARAMETERS-----).*/m, '\1'
- output << "\n"
- write_file!(:dh_params, output)
- else
- log 0, 'Generating DH parameters (takes a REALLY long time)...'
- output = OpenSSL::PKey::DH.generate(3248).to_pem
- write_file!(:dh_params, output)
+ c.desc 'Creates a Diffie-Hellman parameter file' # (needed for server-side of some TLS connections)
+ c.command :dh do |c|
+ c.action do |global_options,options,args|
+ long_running do
+ if cmd_exists?('certtool')
+ log 0, 'Generating DH parameters (takes a long time)...'
+ output = assert_run!('certtool --generate-dh-params --sec-param high')
+ output.sub! /.*(-----BEGIN DH PARAMETERS-----.*-----END DH PARAMETERS-----).*/m, '\1'
+ output << "\n"
+ write_file!(:dh_params, output)
+ else
+ log 0, 'Generating DH parameters (takes a REALLY long time)...'
+ output = OpenSSL::PKey::DH.generate(3248).to_pem
+ write_file!(:dh_params, output)
+ end
end
end
end
- end
-
- #
- # hints:
- #
- # inspect CSR:
- # openssl req -noout -text -in files/cert/x.csr
- #
- # generate CSR with openssl to see how it compares:
- # openssl req -sha256 -nodes -newkey rsa:2048 -keyout example.key -out example.csr
- #
- # validate a CSR:
- # http://certlogik.com/decoder/
- #
- # nice details about CSRs:
- # http://www.redkestrel.co.uk/Articles/CSR.html
- #
- desc 'Creates a Certificate Signing Request for use in purchasing a commercial x509 certificate'
- command :'init-csr' do |c|
- #c.switch 'sign', :desc => 'additionally creates a cert that is signed by your own CA (recommended only for testing)', :negatable => false
- c.action do |global_options,options,args|
- assert_config! 'provider.domain'
- assert_config! 'provider.name'
- assert_config! 'provider.default_language'
- assert_config! 'provider.ca.server_certificates.bit_size'
- assert_config! 'provider.ca.server_certificates.digest'
- assert_files_missing! [:commercial_key, manager.provider.domain], [:commercial_csr, manager.provider.domain], :msg => 'If you really want to create a new key and CSR, remove these files first.'
- if options[:sign]
- assert_files_exist! :ca_cert, :ca_key, :msg => 'Run init-ca to create them'
- end
- # RSA key
- keypair = CertificateAuthority::MemoryKeyMaterial.new
- log :generating, "%s bit RSA key" % manager.provider.ca.server_certificates.bit_size do
- keypair.generate_key(manager.provider.ca.server_certificates.bit_size)
- write_file! [:commercial_key, manager.provider.domain], keypair.private_key.to_pem
- end
+ #
+ # hints:
+ #
+ # inspect CSR:
+ # openssl req -noout -text -in files/cert/x.csr
+ #
+ # generate CSR with openssl to see how it compares:
+ # openssl req -sha256 -nodes -newkey rsa:2048 -keyout example.key -out example.csr
+ #
+ # validate a CSR:
+ # http://certlogik.com/decoder/
+ #
+ # nice details about CSRs:
+ # http://www.redkestrel.co.uk/Articles/CSR.html
+ #
+ c.desc 'Creates a CSR for use in buying a commercial X.509 certificate'
+ c.command :csr do |c|
+ #c.switch 'sign', :desc => 'additionally creates a cert that is signed by your own CA (recommended only for testing)', :negatable => false
+ c.action do |global_options,options,args|
+ assert_config! 'provider.domain'
+ assert_config! 'provider.name'
+ assert_config! 'provider.default_language'
+ assert_config! 'provider.ca.server_certificates.bit_size'
+ assert_config! 'provider.ca.server_certificates.digest'
+ assert_files_missing! [:commercial_key, manager.provider.domain], [:commercial_csr, manager.provider.domain], :msg => 'If you really want to create a new key and CSR, remove these files first.'
+ if options[:sign]
+ assert_files_exist! :ca_cert, :ca_key, :msg => 'Run `leap cert ca` to create them'
+ end
- # CSR
- dn = CertificateAuthority::DistinguishedName.new
- csr = CertificateAuthority::SigningRequest.new
- dn.common_name = manager.provider.domain
- dn.organization = manager.provider.name[manager.provider.default_language]
- log :generating, "CSR with commonName => '%s', organization => '%s'" % [dn.common_name, dn.organization] do
- csr.distinguished_name = dn
- csr.key_material = keypair
- csr.digest = manager.provider.ca.server_certificates.digest
- request = csr.to_x509_csr
- write_file! [:commercial_csr, manager.provider.domain], csr.to_pem
- end
+ # RSA key
+ keypair = CertificateAuthority::MemoryKeyMaterial.new
+ log :generating, "%s bit RSA key" % manager.provider.ca.server_certificates.bit_size do
+ keypair.generate_key(manager.provider.ca.server_certificates.bit_size)
+ write_file! [:commercial_key, manager.provider.domain], keypair.private_key.to_pem
+ end
- # Sign using our own CA, for use in testing but hopefully not production.
- # It is not that commerical CAs are so secure, it is just that signing your own certs is
- # a total drag for the user because they must click through dire warnings.
- #if options[:sign]
- log :generating, "self-signed x509 server certificate for testing purposes" do
- cert = csr.to_cert
- cert.serial_number.number = cert_serial_number(manager.provider.domain)
- cert.not_before = today
- cert.not_after = years_from_today(1)
- cert.parent = ca_root
- cert.sign! domain_test_signing_profile
- write_file! [:commercial_cert, manager.provider.domain], cert.to_pem
- log "please replace this file with the real certificate you get from a CA using #{Path.relative_path([:commercial_csr, manager.provider.domain])}"
+ # CSR
+ dn = CertificateAuthority::DistinguishedName.new
+ csr = CertificateAuthority::SigningRequest.new
+ dn.common_name = manager.provider.domain
+ dn.organization = manager.provider.name[manager.provider.default_language]
+ log :generating, "CSR with commonName => '%s', organization => '%s'" % [dn.common_name, dn.organization] do
+ csr.distinguished_name = dn
+ csr.key_material = keypair
+ csr.digest = manager.provider.ca.server_certificates.digest
+ request = csr.to_x509_csr
+ write_file! [:commercial_csr, manager.provider.domain], csr.to_pem
end
- #end
+
+ # Sign using our own CA, for use in testing but hopefully not production.
+ # It is not that commerical CAs are so secure, it is just that signing your own certs is
+ # a total drag for the user because they must click through dire warnings.
+ #if options[:sign]
+ log :generating, "self-signed x509 server certificate for testing purposes" do
+ cert = csr.to_cert
+ cert.serial_number.number = cert_serial_number(manager.provider.domain)
+ cert.not_before = today
+ cert.not_after = years_from_today(1)
+ cert.parent = ca_root
+ cert.sign! domain_test_signing_profile
+ write_file! [:commercial_cert, manager.provider.domain], cert.to_pem
+ log "please replace this file with the real certificate you get from a CA using #{Path.relative_path([:commercial_csr, manager.provider.domain])}"
+ end
+ #end
+ end
end
end
diff --git a/lib/leap_cli/commands/compile.rb b/lib/leap_cli/commands/compile.rb
index 9882e6a..45e4f2b 100644
--- a/lib/leap_cli/commands/compile.rb
+++ b/lib/leap_cli/commands/compile.rb
@@ -2,7 +2,7 @@
module LeapCli
module Commands
- desc 'Compile json files to hiera configs'
+ desc 'Compiles node configuration files into hiera files used for deployment'
command :compile do |c|
c.action do |global_options,options,args|
# these must come first
diff --git a/lib/leap_cli/commands/node.rb b/lib/leap_cli/commands/node.rb
index 28e250a..678bebd 100644
--- a/lib/leap_cli/commands/node.rb
+++ b/lib/leap_cli/commands/node.rb
@@ -6,41 +6,43 @@ module LeapCli; module Commands
##
## COMMANDS
##
-
- desc 'not yet implemented... Create a new configuration for a node'
- command :'add-node' do |c|
- c.action do |global_options,options,args|
+ desc 'Node management'
+ command :node do |c|
+ c.desc 'Create a new configuration file for a node'
+ c.command :add do |c|
+ c.action do |global_options,options,args|
+ end
end
- end
- desc 'Bootstraps a node, setting up ssh keys and installing prerequisites'
- arg_name '<node-name>', :optional => false, :multiple => false
- command :'init-node' do |c|
- c.switch 'echo', :desc => 'if set, passwords are visible as you type them (default is hidden)', :negatable => false
- c.action do |global_options,options,args|
- node = get_node_from_args(args)
- ping_node(node)
- save_public_host_key(node)
- update_compiled_ssh_configs
- ssh_connect(node, :bootstrap => true, :echo => options[:echo]) do |ssh|
- ssh.install_authorized_keys
- ssh.install_prerequisites
+ c.desc 'Bootstraps a node, setting up ssh keys and installing prerequisites'
+ c.arg_name 'node-name', :optional => false, :multiple => false
+ c.command :init do |c|
+ c.switch 'echo', :desc => 'if set, passwords are visible as you type them (default is hidden)', :negatable => false
+ c.action do |global_options,options,args|
+ node = get_node_from_args(args)
+ ping_node(node)
+ save_public_host_key(node)
+ update_compiled_ssh_configs
+ ssh_connect(node, :bootstrap => true, :echo => options[:echo]) do |ssh|
+ ssh.install_authorized_keys
+ ssh.install_prerequisites
+ end
+ log :completed, "node init #{node.name}"
end
- log :completed, "init-node #{node.name}"
end
- end
- desc 'not yet implemented'
- command :'rename-node' do |c|
- c.action do |global_options,options,args|
+ c.desc 'Renames a node file, and all its related files'
+ c.command :mv do |c|
+ c.action do |global_options,options,args|
+ end
end
- end
- desc 'not yet implemented'
- arg_name '<node-name>', :optional => false, :multiple => false
- command :'rm-node' do |c|
- c.action do |global_options,options,args|
- remove_file!()
+ c.desc 'Removes a node file, and all its related files'
+ c.arg_name '<node-name>', :optional => false, :multiple => false
+ c.command :rm do |c|
+ c.action do |global_options,options,args|
+ remove_file!()
+ end
end
end
diff --git a/lib/leap_cli/commands/test.rb b/lib/leap_cli/commands/test.rb
index dc08652..dd505b6 100644
--- a/lib/leap_cli/commands/test.rb
+++ b/lib/leap_cli/commands/test.rb
@@ -1,18 +1,23 @@
module LeapCli; module Commands
- desc 'Creates files needed to run tests'
- command :'init-test' do |c|
- c.action do |global_options,options,args|
- generate_test_client_cert
- generate_test_client_openvpn_config
- end
- end
-
desc 'Run tests'
command :test do |c|
- c.action do |global_options,options,args|
- log 'not yet implemented'
+ c.desc 'Creates files needed to run tests'
+ c.command :init do |c|
+ c.action do |global_options,options,args|
+ generate_test_client_cert
+ generate_test_client_openvpn_config
+ end
end
+
+ c.desc 'Run tests'
+ c.command :run do |c|
+ c.action do |global_options,options,args|
+ log 'not yet implemented'
+ end
+ end
+
+ c.default_command :run
end
private
diff --git a/lib/leap_cli/remote/plugin.rb b/lib/leap_cli/remote/plugin.rb
index 6dafbd8..803ebf9 100644
--- a/lib/leap_cli/remote/plugin.rb
+++ b/lib/leap_cli/remote/plugin.rb
@@ -29,7 +29,7 @@ module LeapCli; module Remote; module Plugin
rescue Capistrano::CommandError => exc
LeapCli::Util.bail! do
exc.hosts.each do |host|
- LeapCli::Util.log :error, "running deploy: node not initialized. Run 'leap init-node #{host}'", :host => host
+ LeapCli::Util.log :error, "running deploy: node not initialized. Run 'leap node init #{host}'", :host => host
end
end
end
diff --git a/lib/lib_ext/gli.rb b/lib/lib_ext/gli.rb
new file mode 100644
index 0000000..0bfdbc1
--- /dev/null
+++ b/lib/lib_ext/gli.rb
@@ -0,0 +1,56 @@
+#
+# print subcommands indented in the main global help screen
+#
+
+module GLI
+ module Commands
+ module HelpModules
+ class GlobalHelpFormat
+ SUB_CMD_INDENT = " "
+ def format
+ program_desc = @app.program_desc
+ program_long_desc = @app.program_long_desc
+ if program_long_desc
+ wrapper = @wrapper_class.new(Terminal.instance.size[0],4)
+ program_long_desc = "\n #{wrapper.wrap(program_long_desc)}\n\n" if program_long_desc
+ else
+ program_long_desc = "\n"
+ end
+
+ # build a list of commands, sort them so the commands with subcommands are at the bottom
+ commands = @sorter.call(@app.commands_declaration_order.reject(&:nodoc)).sort do |a,b|
+ if a.commands.any? && b.commands.any?; a.name <=> b.name
+ elsif a.commands.any?; 1
+ elsif b.commands.any?; -1
+ else; a.name <=> b.name
+ end
+ end
+
+ # build a list of command info ([name, description]), including subcommands if appropriate
+ command_info_list = []
+ commands.each do |command|
+ name = [command.name, Array(command.aliases)].flatten.join(', ')
+ command_info_list << [name, command.description]
+ if command.commands.any?
+ @sorter.call(command.commands_declaration_order).each do |cmd|
+ if command.get_default_command == cmd.name
+ command_info_list << [SUB_CMD_INDENT + cmd.names,cmd.description + " (default)"]
+ else
+ command_info_list << [SUB_CMD_INDENT + cmd.names,cmd.description]
+ end
+ end
+ end
+ end
+
+ # display
+ command_formatter = ListFormatter.new(command_info_list, @wrapper_class)
+ stringio = StringIO.new
+ command_formatter.output(stringio)
+ commands = stringio.string
+ global_option_descriptions = OptionsFormatter.new(global_flags_and_switches,@wrapper_class).format
+ GLOBAL_HELP.result(binding)
+ end
+ end
+ end
+ end
+end
diff --git a/test/leap_platform/provider_base/services/openvpn.json b/test/leap_platform/provider_base/services/openvpn.json
index 65e7cad..8c0965e 100644
--- a/test/leap_platform/provider_base/services/openvpn.json
+++ b/test/leap_platform/provider_base/services/openvpn.json
@@ -13,6 +13,6 @@
"nat": true,
"ca_crt": "= file :ca_cert",
"ca_key": "= file :ca_key",
- "dh": "= file :dh_params, :missing => 'Diffie-Hellman parameters. Run `leap init-dh`'"
+ "dh": "= file :dh_params, :missing => 'Diffie-Hellman parameters. Run `leap cert dh` to create it'"
}
}