]> gitweb.fluxo.info Git - leap/leap_cli.git/commitdiff
command name shuffle -- grouped more commands together as subcommands
authorelijah <elijah@riseup.net>
Wed, 28 Nov 2012 22:08:39 +0000 (14:08 -0800)
committerelijah <elijah@riseup.net>
Wed, 28 Nov 2012 22:08:39 +0000 (14:08 -0800)
bin/leap
lib/core_ext/string.rb [new file with mode: 0644]
lib/leap_cli.rb
lib/leap_cli/commands/ca.rb
lib/leap_cli/commands/compile.rb
lib/leap_cli/commands/node.rb
lib/leap_cli/commands/test.rb
lib/leap_cli/remote/plugin.rb
lib/lib_ext/gli.rb [new file with mode: 0644]
test/leap_platform/provider_base/services/openvpn.json

index 461ff3c985889c18d7c56b78671f35d16f7e411e..690a5609525b2d2579d49646e7610cf878e8a46e 100755 (executable)
--- 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 (file)
index 0000000..07af8e5
--- /dev/null
@@ -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
index 74c58325fdb438d584e6a407a4de12634ea4cb88..3f35cd48a6933f15f033d856a8b8c1698f492334 100644 (file)
@@ -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
-
index 05bdb2bafa72b1ce71009c67ce46e166152ec61c..f471b5aa416587b8b6607143e9dce41d3318d236 100644 (file)
@@ -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
 
index 9882e6a45586e08d9ced00f7ee25b938e16f05ac..45e4f2b3d0f7dceb11616f615b834ed74625726d 100644 (file)
@@ -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
index 28e250a117a3a7ce7bb53a94a40e7fa2cf64c773..678bebd4cad585f2ee6b8282bc7d1810462c58df 100644 (file)
@@ -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
 
index dc086522281e734a04b8752f0e57c4a11e6673f6..dd505b6f3fbb3e1dbed6f4b2d7d61fb3f083514e 100644 (file)
@@ -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
index 6dafbd865e489f6b2a1b4f1cb36d604f87a25e5c..803ebf93b297fddeaf2f5b8278e70b4d3b29b981 100644 (file)
@@ -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 (file)
index 0000000..0bfdbc1
--- /dev/null
@@ -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
index 65e7cad8dbae0823853b3cd8f2ade31728d53e48..8c0965e36770e914706f4918f11b00f64392a5c4 100644 (file)
@@ -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'"
   }
 }