]> gitweb.fluxo.info Git - leap/leap_cli.git/commitdiff
vagrant support
authorelijah <elijah@riseup.net>
Fri, 9 Nov 2012 09:22:48 +0000 (01:22 -0800)
committerelijah <elijah@riseup.net>
Fri, 9 Nov 2012 09:22:48 +0000 (01:22 -0800)
17 files changed:
.gitignore
lib/leap_cli.rb
lib/leap_cli/commands/shell.rb
lib/leap_cli/commands/util.rb
lib/leap_cli/commands/vagrant.rb [new file with mode: 0644]
lib/leap_cli/config/manager.rb
lib/leap_cli/config/object.rb
lib/leap_cli/config/object_list.rb
lib/leap_cli/path.rb
lib/leap_cli/util/remote_command.rb [new file with mode: 0644]
test/provider/common.json
test/provider/nodes/couch1.json
test/provider/nodes/vpn1.json
test/provider/provider.json
vendor/vagrant_ssh_keys/README [new file with mode: 0644]
vendor/vagrant_ssh_keys/vagrant.key [new file with mode: 0644]
vendor/vagrant_ssh_keys/vagrant.pub [new file with mode: 0644]

index 61a56c8dc9ef598ac8a0eb7d03bc14531d6d5a0d..5038bad12b882858e42055cea5e6ba07672252c0 100644 (file)
@@ -2,4 +2,6 @@ Gemfile.lock
 pkg
 junk
 test/provider/hiera
+.vagrant
+Vagrantfile
 
index 728e501483c535c6d4be005923969876d1b41d91..5ed5033e63cc4a178c7eae160b6e70a79abd5bfe 100644 (file)
@@ -9,6 +9,7 @@ require 'leap_cli/init'
 require 'leap_cli/path'
 require 'leap_cli/util'
 require 'leap_cli/util/secret'
+require 'leap_cli/util/remote_command'
 
 require 'leap_cli/log'
 require 'leap_cli/ssh_key'
index a84c671bc7827685a5b7e20b1724fed9b0b460e8..f73a706bd81c4438d115d5701c37289c99cf0406 100644 (file)
@@ -2,10 +2,19 @@ module LeapCli; module Commands
 
   desc 'Log in to the specified node with an interactive shell'
   arg_name '<node-name>', :optional => false, :multiple => false
-  command :shell, :ssh do |c|
+  command :ssh do |c|
     c.action do |global_options,options,args|
       node = get_node_from_args(args)
-      exec "ssh -l root -o 'HostName=#{node.ip_address}' -o 'HostKeyAlias=#{node.name}' -o 'GlobalKnownHostsFile=#{path(:known_hosts)}' -o 'StrictHostKeyChecking=yes' -p #{node.ssh.port} #{node.name}"
+      options = [
+        "-o 'HostName=#{node.ip_address}'",
+        "-o 'HostKeyAlias=#{node.name}'",
+        "-o 'GlobalKnownHostsFile=#{path(:known_hosts)}'",
+        "-o 'StrictHostKeyChecking=yes'"
+      ]
+      if node.vagrant?
+        options << "-i #{vagrant_ssh_key_file}"
+      end
+      exec "ssh -l root -p #{node.ssh.port} #{options.join(' ')} {node.name}"
     end
   end
 
index 164ce0d90ae4e064a65e83d575cee6bb1d1c8e52..c1da570e342b5b64b3dcd2eff83a8093dbc4d8a5 100644 (file)
@@ -2,6 +2,7 @@ module LeapCli; module Commands
 
   extend self
   extend LeapCli::Util
+  extend LeapCli::Util::RemoteCommand
 
   def path(name)
     Path.named_path(name)
@@ -33,105 +34,6 @@ module LeapCli; module Commands
     end
   end
 
-  #
-  #
-  #
-  # FYI
-  #  Capistrano::Logger::IMPORTANT = 0
-  #  Capistrano::Logger::INFO      = 1
-  #  Capistrano::Logger::DEBUG     = 2
-  #  Capistrano::Logger::TRACE     = 3
-  #
-  def ssh_connect(nodes, options={}, &block)
-    node_list = parse_node_list(nodes)
-
-    cap = new_capistrano
-    cap.logger.level = LeapCli.log_level
-    user = options[:user] || 'root'
-    cap.set :user, user
-    cap.set :ssh_options, ssh_options
-    cap.set :use_sudo, false # we may want to change this in the future
-
-    # # supply drop options
-    # cap.set :puppet_source, [Path.platform, 'puppet'].join('/')
-    # cap.set :puppet_destination, '/root/leap'
-    # #cap.set :puppet_command, 'puppet apply'
-    # cap.set :puppet_lib, "puppet/modules"
-    # cap.set :puppet_parameters, '--confdir=puppet puppet/manifests/site.pp'
-    # #cap.set :puppet_stream_output, false
-    # #puppet apply --confdir=puppet puppet/manifests/site.pp  | grep -v 'warning:.*is deprecated'
-    # #puppet_cmd = "cd #{puppet_destination} && #{sudo_cmd} #{puppet_command} --modulepath=#{puppet_lib} #{puppet_parameters}"
-
-    #
-    # allow password authentication when we are bootstraping a single node.
-    #
-    if options[:bootstrap] && node_list.size == 1
-      hostname = node_list.values.first.name
-
-      # the 'password' block is only called if key auth fails
-      if options[:echo]
-        cap.set(:password) { ask "Root SSH password for #{user}@#{hostname}> " }
-      else
-        cap.set(:password) { Capistrano::CLI.password_prompt " * Typed password will be hidden (use --echo to make it visible)\nRoot SSH password for #{user}@#{hostname}> " }
-      end
-    end
-
-    node_list.each do |name, node|
-      cap.server node.name, :dummy_arg, node_options(node)
-    end
-
-    yield cap
-  end
-
-
-  private
-
-
-  #
-  # For available options, see http://net-ssh.github.com/net-ssh/classes/Net/SSH.html#method-c-start
-  #
-  def ssh_options
-    {
-      :config => false,
-      :global_known_hosts_file => path(:known_hosts),
-      :paranoid => true
-    }
-  end
-
-  #
-  # For notes on advanced ways to set server-specific options, see
-  # http://railsware.com/blog/2011/11/02/advanced-server-definitions-in-capistrano/
-  #
-  def node_options(node)
-    #password_proc = Proc.new {Capistrano::CLI.password_prompt "Root SSH password for #{node.name}"}  # only called if needed
-    {
-      #:password => password_proc,
-      :ssh_options => {
-        :host_key_alias => node.name,
-        :host_name => node.ip_address,
-        :port => node.ssh.port
-      }
-    }
-  end
-
-  def new_capistrano
-    # load once the library files
-    @capistrano_enabled ||= begin
-      require 'capistrano'
-      require 'capistrano/cli'
-      require 'leap_cli/remote/plugin'
-      Capistrano.plugin :leap, LeapCli::Remote::Plugin
-      true
-    end
-
-    # create capistrano instance
-    cap = Capistrano::Configuration.new
-
-    # add tasks to capistrano instance
-    cap.load File.dirname(__FILE__) + '/../remote/tasks.rb'
-
-    return cap
-  end
 
   def parse_node_list(nodes)
     if nodes.is_a? Config::Object
diff --git a/lib/leap_cli/commands/vagrant.rb b/lib/leap_cli/commands/vagrant.rb
new file mode 100644 (file)
index 0000000..587e5e1
--- /dev/null
@@ -0,0 +1,72 @@
+require 'ipaddr'
+
+module LeapCli; module Commands
+
+  desc 'Bring up one or more local virtual machines'
+  arg_name '[node-filter]', :optional => true, :multiple => false
+  command :'local-up' do |c|
+    c.action do |global_options,options,args|
+      vagrant_command("up", args)
+    end
+  end
+
+  desc 'Halt one or more local virtual machines'
+  arg_name '[node-filter]', :optional => true, :multiple => false
+  command :'local-down' do |c|
+    c.action do |global_options,options,args|
+      vagrant_command("halt", args)
+    end
+  end
+
+  desc 'Destroy one or more local virtual machines'
+  arg_name '[node-filter]', :optional => true, :multiple => false
+  command :'local-reset' do |c|
+    c.action do |global_options,options,args|
+      vagrant_command("destroy", args)
+    end
+  end
+
+  public
+
+  def vagrant_ssh_key_file
+    file = File.expand_path('../../../vendor/vagrant_ssh_keys/vagrant.key', File.dirname(__FILE__))
+    Util.assert_files_exist! file
+    return file
+  end
+
+  private
+
+  def vagrant_command(cmd, args)
+    create_vagrant_file
+    nodes = manager.filter(args)[:local => true].field(:name)
+    if nodes.any?
+      execute "cd #{File.dirname(Path.named_path(:vagrantfile))}; vagrant #{cmd} #{nodes.join(' ')}"
+    else
+      bail! "No nodes found"
+    end
+  end
+
+  def execute(cmd)
+    progress2 "Running: #{cmd}"
+    exec cmd
+  end
+
+  def create_vagrant_file
+    lines = []
+    netmask = IPAddr.new('255.255.255.255').mask(manager.provider.vagrant.network.split('/').last).to_s
+    lines << %[Vagrant::Config.run do |config|]
+    manager.each_node do |node|
+      if node.vagrant?
+        lines << %[  config.vm.define :#{node.name} do |config|]
+        lines << %[    config.vm.box = "minimal-wheezy"]
+        lines << %[    config.vm.box_url = "http://cloud.github.com/downloads/leapcode/minimal-debian-vagrant/minimal-wheezy.box"]
+        lines << %[    config.vm.network :hostonly, "#{node.ip_address}", :netmask => "#{netmask}"]
+        lines << %[  end]
+      end
+    end
+    lines << %[end]
+    lines << ""
+    write_file! :vagrantfile, lines.join("\n")
+  end
+
+end; end
\ No newline at end of file
index 88f21beda899a701794bdafdd1889cabafd7d005..00b4ec51a9407dd5147428e57dbdbe234d5c1876 100644 (file)
@@ -113,6 +113,13 @@ module LeapCli
         nodes[name]
       end
 
+      #
+      # yields each node, in sorted order
+      #
+      def each_node(&block)
+        nodes.each_node &block
+      end
+
       private
 
       def load_all_json(pattern)
@@ -232,6 +239,14 @@ module LeapCli
         end
       end
 
+      #
+      # TODO: apply JSON spec
+      #
+      PRIVATE_IP_RANGES = /(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/
+      def validate_provider(provider)
+        Util::assert! provider.vagrant.network =~ PRIVATE_IP_RANGES, 'provider.json error: vagrant.network is not a local private network'
+      end
+
     end
   end
 end
index ad32f54d1642f6643e83d589bfc2f34ad4110629..731f3ff8efad15ce5eb28c5fbbc40167839aeada 100644 (file)
@@ -144,6 +144,20 @@ module LeapCli
         self
       end
 
+      ##
+      ## NODE SPECIFIC
+      ## maybe these should be moved to a Node class.
+      ##
+
+      #
+      # 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)
+      end
+
       ##
       ## MACROS
       ## these are methods used when eval'ing a value in the .json configuration
index 708afc11e63e154b77054ee8aed2e438f5b06e5f..b0839ca28efa40791f39426e359d98673e7ecfdc 100644 (file)
@@ -46,6 +46,11 @@ module LeapCli
         end
       end
 
+      def each_node(&block)
+        self.keys.sort.each do |node_name|
+          yield self[node_name]
+        end
+      end
 
       # def <<(object)
       #   if object.is_a? Config::ObjectList
index 6d68546835e5107542a0ae5ca29c8d9d6bcb034a..48b0d11c1b76fe88786132d20c33709a30854bbd 100644 (file)
@@ -29,7 +29,8 @@ module LeapCli; module Path
     :ca_cert          => 'files/ca/ca.crt',
     :dh_params        => 'files/ca/dh.pem',
     :node_x509_key    => 'files/nodes/#{arg}/#{arg}.key',
-    :node_x509_cert   => 'files/nodes/#{arg}/#{arg}.crt'
+    :node_x509_cert   => 'files/nodes/#{arg}/#{arg}.crt',
+    :vagrantfile      => 'test/Vagrantfile'
   }
 
   #
diff --git a/lib/leap_cli/util/remote_command.rb b/lib/leap_cli/util/remote_command.rb
new file mode 100644 (file)
index 0000000..118a65e
--- /dev/null
@@ -0,0 +1,98 @@
+module LeapCli; module Util; module RemoteCommand
+  extend self
+
+  #
+  # FYI
+  #  Capistrano::Logger::IMPORTANT = 0
+  #  Capistrano::Logger::INFO      = 1
+  #  Capistrano::Logger::DEBUG     = 2
+  #  Capistrano::Logger::TRACE     = 3
+  #
+  def ssh_connect(nodes, options={}, &block)
+    node_list = parse_node_list(nodes)
+
+    cap = new_capistrano
+    cap.logger.level = LeapCli.log_level
+    user = options[:user] || 'root'
+    cap.set :user, user
+    cap.set :ssh_options, ssh_options # ssh options common to all nodes
+    cap.set :use_sudo, false          # we may want to change this in the future
+
+    # Allow password authentication when we are bootstraping a single node
+    # (and key authentication fails).
+    if options[:bootstrap] && node_list.size == 1
+      hostname = node_list.values.first.name
+      if options[:echo]
+        cap.set(:password) { ask "Root SSH password for #{user}@#{hostname}> " }
+      else
+        cap.set(:password) { Capistrano::CLI.password_prompt " * Typed password will be hidden (use --echo to make it visible)\nRoot SSH password for #{user}@#{hostname}> " }
+      end
+    end
+
+    node_list.each do |name, node|
+      cap.server node.name, :dummy_arg, node_options(node)
+    end
+
+    yield cap
+  end
+
+  private
+
+  #
+  # For available options, see http://net-ssh.github.com/net-ssh/classes/Net/SSH.html#method-c-start
+  #
+  def ssh_options
+    {
+      :config => false,
+      :global_known_hosts_file => path(:known_hosts),
+      :paranoid => true
+    }
+  end
+
+  #
+  # For notes on advanced ways to set server-specific options, see
+  # http://railsware.com/blog/2011/11/02/advanced-server-definitions-in-capistrano/
+  #
+  # if, in the future, we want to do per-node password options, it would be done like so:
+  #
+  #  password_proc = Proc.new {Capistrano::CLI.password_prompt "Root SSH password for #{node.name}"}
+  #  return {:password => password_proc}
+  #
+  def node_options(node)
+    {
+      :ssh_options => {
+        :host_key_alias => node.name,
+        :host_name => node.ip_address,
+        :port => node.ssh.port
+      }.merge(contingent_ssh_options_for_node(node))
+    }
+  end
+
+  def new_capistrano
+    # load once the library files
+    @capistrano_enabled ||= begin
+      require 'capistrano'
+      require 'capistrano/cli'
+      require 'leap_cli/remote/plugin'
+      Capistrano.plugin :leap, LeapCli::Remote::Plugin
+      true
+    end
+
+    # create capistrano instance
+    cap = Capistrano::Configuration.new
+
+    # add tasks to capistrano instance
+    cap.load File.dirname(__FILE__) + '/../remote/tasks.rb'
+
+    return cap
+  end
+
+  def contingent_ssh_options_for_node(node)
+    if node.vagrant?
+      {:keys => [vagrant_ssh_key_file]}
+    else
+      {}
+    end
+  end
+
+end; end; end
\ No newline at end of file
index 7504e86c5ea2322fecb395744ce45802fda1e430..85a93cbfc03bab1bd7518af2dd354c9e50b2bce4 100644 (file)
@@ -21,5 +21,6 @@
     "use": false,
     "cert": "= x509.use ? file(:node_x509_cert) : nil",
     "key": "= x509.use ? file(:node_x509_key) : nil"
-  }
+  },
+  "local": "= self.vagrant?"
 }
index fe5d7e565c9cb1fd86bba7320a8bc4256858c323..d246b9901be9e25568c0eda159c6967479afa653 100644 (file)
@@ -1,4 +1,4 @@
 {
   "services": "couchdb",
-  "ip_address": "245.2.45.42"
+  "ip_address": "10.5.5.2"
 }
\ No newline at end of file
index 1c58a1bad9bd4274a6e1378c2af59c01a54e4efa..5115cb24953e4f5d7b5afb8cad3dbd8d6ac9ab4e 100644 (file)
@@ -1,6 +1,6 @@
 {
   "services": "openvpn",
-  "ip_address": "2.2.2.2",
+  "ip_address": "10.5.5.3",
   "tags": "production",
   "openvpn": {
     "gateway_address": "3.3.3.3",
index e65eebebf0360bcd7ffee31ea0892ee4395bc78f..6e7618ff1d1d1a4c4a47b6487049f8eef87a93d8 100644 (file)
@@ -23,5 +23,8 @@
       "bit_size": 3248,
       "life_span": "1y"
     }
+  },
+  "vagrant":{
+    "network":"10.5.5.0/24"
   }
 }
\ No newline at end of file
diff --git a/vendor/vagrant_ssh_keys/README b/vendor/vagrant_ssh_keys/README
new file mode 100644 (file)
index 0000000..905756d
--- /dev/null
@@ -0,0 +1,5 @@
+# Insecure Keypair
+
+These keys are the "insecure" public/private keypair commonly used by vagrant
+base boxes so that vagrant installations can automatically SSH into the boxes.
+
diff --git a/vendor/vagrant_ssh_keys/vagrant.key b/vendor/vagrant_ssh_keys/vagrant.key
new file mode 100644 (file)
index 0000000..7d6a083
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI
+w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP
+kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2
+hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO
+Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW
+yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd
+ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1
+Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf
+TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK
+iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A
+sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf
+4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP
+cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk
+EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN
+CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX
+3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG
+YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj
+3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+
+dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz
+6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC
+P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF
+llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ
+kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH
++vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ
+NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s=
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/vagrant_ssh_keys/vagrant.pub b/vendor/vagrant_ssh_keys/vagrant.pub
new file mode 100644 (file)
index 0000000..18a9c00
--- /dev/null
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key