]> gitweb.fluxo.info Git - leap/leap_cli.git/commitdiff
added commands 'node add' 'node rm' and 'node mv'
authorelijah <elijah@riseup.net>
Sun, 9 Dec 2012 04:02:27 +0000 (20:02 -0800)
committerelijah <elijah@riseup.net>
Sun, 9 Dec 2012 04:02:27 +0000 (20:02 -0800)
lib/leap_cli/commands/deploy.rb
lib/leap_cli/commands/node.rb
lib/leap_cli/commands/pre.rb
lib/leap_cli/commands/vagrant.rb
lib/leap_cli/config/object.rb
lib/leap_cli/path.rb
lib/leap_cli/util.rb
test/leap_platform/provider_base/common.json

index bee09a06f5adbfa3dfe782cf971497f1d62f0cc1..f94465f161482518fd13a65deb573f8dd9a49c2e 100644 (file)
@@ -16,6 +16,10 @@ module LeapCli
           end
         end
 
+        nodes.each_node do |node|
+          assert_files_exist! Path.named_path([:hiera, node.name]), :msg => 'try running `leap compile`'
+        end
+
         ssh_connect(nodes) do |ssh|
           ssh.leap.assert_initialized
 
index 9bf27e2c43da38cb4bd9c826d63004d80328718d..f208f87bb6030ac5a67cbb462ef9bbe4f2e3ee38 100644 (file)
@@ -10,16 +10,41 @@ module LeapCli; module Commands
   desc 'Node management'
   command :node do |node|
     node.desc 'Create a new configuration file for a node'
+    node.long_desc ["If specified, the optional argument seed-options can be used to seed values in the node configuration file.",
+                    "The format is property_name:value.",
+                    "For example: `leap node add web1 ip_address:1.2.3.4 services:webapp`.",
+                    "To set nested properties, property name can contain '.', like so: `leap node add web1 ssh.port:44`",
+                    "To set multiple values for a single property, use ',', like so: `leap node add mynode services:webapp,dns`"].join("\n\n")
+    node.arg_name '<node-name> [seed-options]' # , :optional => false, :multiple => false
     node.command :add do |add|
+      add.switch :local, :desc => 'Make a local testing node (by automatically assigning the next available local IP address). Local nodes are run as virtual machines on your computer.', :negatable => false
       add.action do |global_options,options,args|
-        log 'not yet implemented'
+        # argument sanity checks
+        name = args.first
+        assert! name, 'No <node-name> specified.'
+        assert! name =~ /^[0-9a-z_-]+$/, "illegal characters used in node name '#{name}'"
+        assert_files_missing! [:node_config, node.name]
+
+        # create and seed new node
+        node = Config::Object.new
+        if options[:local]
+          node['ip_address'] = pick_next_vagrant_ip_address
+        end
+        seed_node_data(node, args[1..-1])
+
+        # write the file
+        write_file! [:node_config, name], node.dump_json + "\n"
       end
     end
 
-    node.desc 'Bootstraps a node, setting up ssh keys and installing prerequisites'
-    node.arg_name 'node-name', :optional => false, :multiple => false
+    node.desc 'Bootstraps a node, setting up SSH keys and installing prerequisite packages'
+    node.long_desc "This command prepares a server to be used with the LEAP Platform by saving the server's SSH host key, " +
+                   "copying the authorized_keys file, and installing packages that are required for deploying. " +
+                   "Node init must be run before deploying to a server, and the server must be running and available via the network. " +
+                   "This command only needs to be run once, but there is no harm in running it multiple times."
+    node.arg_name '<node-name>' #, :optional => false, :multiple => false
     node.command :init do |init|
-      init.switch 'echo', :desc => 'if set, passwords are visible as you type them (default is hidden)', :negatable => false
+      init.switch 'echo', :desc => 'If set, passwords are visible as you type them (default is hidden)', :negatable => false
       init.action do |global_options,options,args|
         node = get_node_from_args(args)
         ping_node(node)
@@ -34,17 +59,30 @@ module LeapCli; module Commands
     end
 
     node.desc 'Renames a node file, and all its related files'
+    node.arg_name '<old-name> <new-name>'
     node.command :mv do |mv|
       mv.action do |global_options,options,args|
-        log 'not yet implemented'
+        node = get_node_from_args(args)
+        new_name = args.last
+        ensure_dir [:node_files_dir, new_name]
+        Path::NODE_PATHS.each do |path|
+          rename_file! [path, node.name], [path, new_name]
+        end
+        remove_directory! [:node_files_dir, node.name]
       end
     end
 
     node.desc 'Removes a node file, and all its related files'
-    node.arg_name '<node-name>', :optional => false, :multiple => false
+    node.arg_name '<node-name>' #:optional => false #, :multiple => false
     node.command :rm do |rm|
       rm.action do |global_options,options,args|
-        log 'not yet implemented'
+        node = get_node_from_args(args)
+        (Path::NODE_PATHS + [:node_files_dir]).each do |path|
+          remove_file! [path, node.name]
+        end
+        if node.vagrant?
+          vagrant_command("destroy --force", [node.name])
+        end
       end
     end
   end
@@ -135,4 +173,26 @@ module LeapCli; module Commands
     assert_run!("ping -W 1 -c 1 #{node.ip_address}", "Could not ping #{node.name} (address #{node.ip_address}). Try again, we only send a single ping.")
   end
 
+  def seed_node_data(node, args)
+    args.each do |seed|
+      key, value = seed.split(':')
+      if value =~ /,/
+        value = value.split(',')
+      end
+      assert! key =~ /^[0-9a-z\._]+$/, "illegal characters used in property '#{key}'"
+      if key =~ /\./
+        key_parts = key.split('.')
+        final_key = key_parts.pop
+        current_object = node
+        key_parts.each do |key_part|
+          current_object[key_part] = Config::Object.new
+          current_object = current_object[key_part]
+        end
+        current_object[final_key] = value
+      else
+        node[key] = value
+      end
+    end
+  end
+
 end; end
\ No newline at end of file
index cae787e369dc3703d95bedcfb2b02b1f3c3e2c48..346814b60b9d2dbb22674df79ad2665f229a4267 100644 (file)
@@ -39,6 +39,11 @@ module LeapCli
         bail! { log :missing, "platform directory '#{Path.platform}'" }
       end
 
+      #
+      # load all the nodes everything
+      #
+      manager
+
       #
       # check requirements
       #
index cd3e71bbc46a95b8e6815070a35da656b1d2b069..a9c2928385cff3fbabe2ca1ad58fa9da26bcfd2d 100644 (file)
@@ -10,7 +10,6 @@ module LeapCli; module Commands
     local.arg_name 'node-filter', :optional => true #, :multiple => false
     local.command :start do |start|
       start.action do |global_options,options,args|
-        vagrant_setup
         vagrant_command(["up", "sandbox on"], args)
       end
     end
@@ -19,7 +18,6 @@ module LeapCli; module Commands
     local.arg_name 'node-filter', :optional => true #, :multiple => false
     local.command :stop do |stop|
       stop.action do |global_options,options,args|
-        vagrant_setup
         vagrant_command("halt", args)
       end
     end
@@ -28,7 +26,6 @@ module LeapCli; module Commands
     local.arg_name 'node-filter', :optional => true #, :multiple => false
     local.command :reset do |reset|
       reset.action do |global_options,options,args|
-        vagrant_setup
         vagrant_command("sandbox rollback", args)
       end
     end
@@ -37,7 +34,6 @@ module LeapCli; module Commands
     local.arg_name 'node-filter', :optional => true #, :multiple => false
     local.command :destroy do |destroy|
       destroy.action do |global_options,options,args|
-        vagrant_setup
         vagrant_command("destroy", args)
       end
     end
@@ -46,7 +42,6 @@ module LeapCli; module Commands
     local.arg_name 'node-filter', :optional => true #, :multiple => false
     local.command :status do |status|
       status.action do |global_options,options,args|
-        vagrant_setup
         vagrant_command("status", args)
       end
     end
@@ -66,18 +61,10 @@ module LeapCli; module Commands
     return file_path
   end
 
-  private
-
-  def vagrant_setup
-    assert_bin! 'vagrant', 'run "sudo gem install vagrant"'
-    unless `vagrant gem which sahara`.chars.any?
-      log :installing, "vagrant plugin 'sahara'"
-      assert_run! 'vagrant gem install sahara'
-    end
-    create_vagrant_file
-  end
+  protected
 
   def vagrant_command(cmds, args)
+    vagrant_setup
     cmds = cmds.to_a
     assert_config! 'provider.vagrant.network'
     if args.empty?
@@ -99,6 +86,17 @@ module LeapCli; module Commands
     end
   end
 
+  private
+
+  def vagrant_setup
+    assert_bin! 'vagrant', 'run "sudo gem install vagrant"'
+    unless `vagrant gem which sahara`.chars.any?
+      log :installing, "vagrant plugin 'sahara'"
+      assert_run! 'vagrant gem install sahara'
+    end
+    create_vagrant_file
+  end
+
   def execute(cmd)
     log 2, :run, cmd
     exec cmd
@@ -123,4 +121,15 @@ module LeapCli; module Commands
     write_file! :vagrantfile, lines.join("\n")
   end
 
+  def pick_next_vagrant_ip_address
+    taken_ips = manager.nodes[:local => true].field(:ip_address)
+    if taken_ips.any?
+      highest_ip = taken_ips.map{|ip| IPAddr.new(ip)}.max
+      new_ip = highest_ip.succ
+    else
+      new_ip = IPAddr.new(manager.provider.vagrant.network).succ.succ
+    end
+    return new_ip.to_s
+  end
+
 end; end
\ No newline at end of file
index bbaa6f40dbc3ecd78231adce9d297a88a7d70c8c..155b51f26339f4b4c06a07ce3d0a0044b25f6957 100644 (file)
@@ -199,9 +199,18 @@ module LeapCli
       # returns true if this node has an ip address in the range of the vagrant network
       #
       def vagrant?
-        vagrant_range = IPAddr.new @manager.provider.vagrant.network
-        ip_address    = IPAddr.new @node.ip_address
-        vagrant_range.include?(ip_address)
+        begin
+          vagrant_range = IPAddr.new @manager.provider.vagrant.network
+        rescue ArgumentError => exc
+          Util::bail! { Util::log :invalid, "ip address '#{@node.ip_address}' vagrant.network" }
+        end
+
+        begin
+          ip_address = IPAddr.new @node.get('ip_address')
+        rescue ArgumentError => exc
+          Util::log :warning, "invalid ip address '#{@node.get('ip_address')}' for node '#{@node.name}'"
+        end
+        return vagrant_range.include?(ip_address)
       end
 
       ##
index 20994f4caa37b0a2502303051c2b5a42358b1934..43f2edc7b5f16579d2fe22ffffe6d45448e2c54e 100644 (file)
@@ -2,6 +2,9 @@ require 'fileutils'
 
 module LeapCli; module Path
 
+  #
+  # all the named paths, relative to provider directory.
+  #
   NAMED_PATHS = {
     # directories
     :hiera_dir        => 'hiera',
@@ -23,16 +26,9 @@ module LeapCli; module Path
     :provider_json_template => 'files/service-definitions/provider.json.erb',
     :eip_service_json_template => 'files/service-definitions/eip-service.json.erb',
 
-    # input data files
-    :commercial_cert  => 'files/cert/#{arg}.crt',
-    :commercial_key   => 'files/cert/#{arg}.key',
-    :commercial_csr   => 'files/cert/#{arg}.csr',
-
     # output files
     :user_ssh         => 'users/#{arg}/#{arg}_ssh.pub',
     :user_pgp         => 'users/#{arg}/#{arg}_pgp.pub',
-    :hiera            => 'hiera/#{arg}.yaml',
-    :node_ssh_pub_key => 'files/nodes/#{arg}/#{arg}_ssh.pub',
     :known_hosts      => 'files/ssh/known_hosts',
     :authorized_keys  => 'files/ssh/authorized_keys',
     :ca_key           => 'files/ca/ca.key',
@@ -42,10 +38,14 @@ module LeapCli; module Path
     :commercial_csr   => 'files/cert/#{arg}.csr',
     :commercial_cert  => 'files/cert/#{arg}.crt',
     :commercial_ca_cert  => 'files/cert/commercial_ca.crt',
-    :node_x509_key       => 'files/nodes/#{arg}/#{arg}.key',
-    :node_x509_cert      => 'files/nodes/#{arg}/#{arg}.crt',
     :vagrantfile         => 'test/Vagrantfile',
 
+    # node output files
+    :hiera            => 'hiera/#{arg}.yaml',
+    :node_ssh_pub_key => 'files/nodes/#{arg}/#{arg}_ssh.pub',
+    :node_x509_key    => 'files/nodes/#{arg}/#{arg}.key',
+    :node_x509_cert   => 'files/nodes/#{arg}/#{arg}.crt',
+
     # testing files
     :test_client_key     => 'test/cert/client.key',
     :test_client_cert    => 'test/cert/client.crt',
@@ -53,6 +53,13 @@ module LeapCli; module Path
     :test_client_openvpn_template => 'test/openvpn/client.ovpn.erb'
   }
 
+  #
+  # paths that take node name as the argument
+  #
+  NODE_PATHS = [
+    :node_config, :hiera, :node_x509_cert, :node_x509_key, :node_ssh_pub_key
+  ]
+
   def self.platform
     @platform
   end
index 98c3002f90ab777eec189556bd87a40b8e4d95de..d12c5a67ef012a8dbbbc05d05caa46f3ec08bb00 100644 (file)
@@ -28,9 +28,9 @@ module LeapCli
         LeapCli.log_level = 3
         yield
       elsif message
-        puts message
+        log 0, message
       end
-      log :bail, ""
+      log 0, :bail, ""
       raise SystemExit.new
     end
 
@@ -105,11 +105,11 @@ module LeapCli
 
     def assert_config!(conf_path)
       value = nil
-      begin
+      #begin
         value = manager.instance_eval(conf_path)
-      rescue NoMethodError
-      rescue NameError
-      end
+      #rescue NoMethodError
+      #rescue NameError
+      #end
       assert! !value.nil? && value != "REQUIRED" do
         log :missing, "required configuration value for #{conf_path}"
       end
@@ -199,19 +199,39 @@ module LeapCli
     def remove_file!(filepath)
       filepath = Path.named_path(filepath)
       if File.exists?(filepath)
-        File.unlink(filepath)
-        log :removed, filepath
+        if File.directory?(filepath)
+          remove_directory!(filepath)
+        else
+          begin
+            File.unlink(filepath)
+            log :removed, filepath
+          rescue Exception => exc
+            bail! do
+              log :failed, "to remove file #{filepath}"
+              log "error message: " + exc.to_s
+            end
+          end
+        end
       end
     end
 
     def remove_directory!(filepath)
       filepath = Path.named_path(filepath)
       if filepath !~ /^#{Regexp.escape(Path.provider)}/ || filepath =~ /\.\./
-        raise "sanity check on rm -r did not pass for #{filepath}"
+        bail! "sanity check on rm -r did not pass for #{filepath}"
       end
       if File.directory?(filepath)
-        FileUtils.rm_r(filepath)
-        log :removed, filepath
+        begin
+          FileUtils.rm_r(filepath)
+          log :removed, filepath
+        rescue Exception => exc
+          bail! do
+            log :failed, "to remove directory #{filepath}"
+            log "error message: " + exc.to_s
+          end
+        end
+      else
+        log :failed, "to remove '#{filepath}', it is not a directory"
       end
     end
 
@@ -237,6 +257,21 @@ module LeapCli
       end
     end
 
+    def rename_file!(oldpath, newpath)
+      oldpath = Path.named_path(oldpath)
+      newpath = Path.named_path(newpath)
+      if File.exists? newpath
+        log :skipping, "#{Path.relative_path(newpath)}, file already exists"
+        return
+      end
+      if !File.exists? oldpath
+        log :skipping, "#{Path.relative_path(oldpath)}, file is missing"
+        return
+      end
+      FileUtils.mv oldpath, newpath
+      log :moved, "#{Path.relative_path(oldpath)} to #{Path.relative_path(newpath)}"
+    end
+
     def cmd_exists?(cmd)
       `which #{cmd}`.strip.chars.any?
     end
index 3d44269eada336d0d802270a45e0d728e32c13ec..f5093a5a7b27996898c7f73108f0544741d098e7 100644 (file)
@@ -18,8 +18,8 @@
   },
   "x509": {
     "use": false,
-    "cert": "= x509.use ? file(:node_x509_cert, :missing => 'x509 certificate for node $node. Run `leap update-cert`') : nil",
-    "key": "= x509.use ? file(:node_x509_key, :missing => 'x509 key for node $node. Run `leap update-cert`') : nil"
+    "cert": "= x509.use ? file(:node_x509_cert, :missing => 'x509 certificate for node $node. Run `leap cert update`') : nil",
+    "key": "= x509.use ? file(:node_x509_key, :missing => 'x509 key for node $node. Run `leap cert update`') : nil"
   },
   "local": false
 }