diff options
| author | elijah <elijah@riseup.net> | 2012-12-08 20:02:27 -0800 | 
|---|---|---|
| committer | elijah <elijah@riseup.net> | 2012-12-08 20:02:27 -0800 | 
| commit | 8572dfd59c21d2032b030adc9dc9a973c6e1c3f5 (patch) | |
| tree | 3d3fb221339ddf4fa0037cbd88133431307f2e4e /lib | |
| parent | 155d744f6c6f94709925f0674f510b3064b6608e (diff) | |
| download | leap_cli-8572dfd59c21d2032b030adc9dc9a973c6e1c3f5.tar.gz leap_cli-8572dfd59c21d2032b030adc9dc9a973c6e1c3f5.tar.bz2  | |
added commands 'node add' 'node rm' and 'node mv'
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/leap_cli/commands/deploy.rb | 4 | ||||
| -rw-r--r-- | lib/leap_cli/commands/node.rb | 74 | ||||
| -rw-r--r-- | lib/leap_cli/commands/pre.rb | 5 | ||||
| -rw-r--r-- | lib/leap_cli/commands/vagrant.rb | 39 | ||||
| -rw-r--r-- | lib/leap_cli/config/object.rb | 15 | ||||
| -rw-r--r-- | lib/leap_cli/path.rb | 25 | ||||
| -rw-r--r-- | lib/leap_cli/util.rb | 57 | 
7 files changed, 174 insertions, 45 deletions
diff --git a/lib/leap_cli/commands/deploy.rb b/lib/leap_cli/commands/deploy.rb index bee09a0..f94465f 100644 --- a/lib/leap_cli/commands/deploy.rb +++ b/lib/leap_cli/commands/deploy.rb @@ -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 diff --git a/lib/leap_cli/commands/node.rb b/lib/leap_cli/commands/node.rb index 9bf27e2..f208f87 100644 --- a/lib/leap_cli/commands/node.rb +++ b/lib/leap_cli/commands/node.rb @@ -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 diff --git a/lib/leap_cli/commands/pre.rb b/lib/leap_cli/commands/pre.rb index cae787e..346814b 100644 --- a/lib/leap_cli/commands/pre.rb +++ b/lib/leap_cli/commands/pre.rb @@ -40,6 +40,11 @@ module LeapCli        end        # +      # load all the nodes everything +      # +      manager + +      #        # check requirements        #        REQUIREMENTS.each do |key| diff --git a/lib/leap_cli/commands/vagrant.rb b/lib/leap_cli/commands/vagrant.rb index cd3e71b..a9c2928 100644 --- a/lib/leap_cli/commands/vagrant.rb +++ b/lib/leap_cli/commands/vagrant.rb @@ -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 diff --git a/lib/leap_cli/config/object.rb b/lib/leap_cli/config/object.rb index bbaa6f4..155b51f 100644 --- a/lib/leap_cli/config/object.rb +++ b/lib/leap_cli/config/object.rb @@ -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        ## diff --git a/lib/leap_cli/path.rb b/lib/leap_cli/path.rb index 20994f4..43f2edc 100644 --- a/lib/leap_cli/path.rb +++ b/lib/leap_cli/path.rb @@ -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 diff --git a/lib/leap_cli/util.rb b/lib/leap_cli/util.rb index 98c3002..d12c5a6 100644 --- a/lib/leap_cli/util.rb +++ b/lib/leap_cli/util.rb @@ -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  | 
