aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2012-11-09 01:22:48 -0800
committerelijah <elijah@riseup.net>2012-11-09 01:22:48 -0800
commitfebeb64a801f3b4c72193bc93ee0400dee3a2a0a (patch)
treea31d0efc4f9ef563c3f9e9023d09e9dfb8b954af
parente0471e278c3baf7fc74f288281c7219cbcf0172c (diff)
downloadleap_cli-febeb64a801f3b4c72193bc93ee0400dee3a2a0a.tar.gz
leap_cli-febeb64a801f3b4c72193bc93ee0400dee3a2a0a.tar.bz2
vagrant support
-rw-r--r--.gitignore2
-rw-r--r--lib/leap_cli.rb1
-rw-r--r--lib/leap_cli/commands/shell.rb13
-rw-r--r--lib/leap_cli/commands/util.rb100
-rw-r--r--lib/leap_cli/commands/vagrant.rb72
-rw-r--r--lib/leap_cli/config/manager.rb15
-rw-r--r--lib/leap_cli/config/object.rb14
-rw-r--r--lib/leap_cli/config/object_list.rb5
-rw-r--r--lib/leap_cli/path.rb3
-rw-r--r--lib/leap_cli/util/remote_command.rb98
-rw-r--r--test/provider/common.json3
-rw-r--r--test/provider/nodes/couch1.json2
-rw-r--r--test/provider/nodes/vpn1.json2
-rw-r--r--test/provider/provider.json3
-rw-r--r--vendor/vagrant_ssh_keys/README5
-rw-r--r--vendor/vagrant_ssh_keys/vagrant.key27
-rw-r--r--vendor/vagrant_ssh_keys/vagrant.pub1
17 files changed, 261 insertions, 105 deletions
diff --git a/.gitignore b/.gitignore
index 61a56c8..5038bad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@ Gemfile.lock
pkg
junk
test/provider/hiera
+.vagrant
+Vagrantfile
diff --git a/lib/leap_cli.rb b/lib/leap_cli.rb
index 728e501..5ed5033 100644
--- a/lib/leap_cli.rb
+++ b/lib/leap_cli.rb
@@ -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'
diff --git a/lib/leap_cli/commands/shell.rb b/lib/leap_cli/commands/shell.rb
index a84c671..f73a706 100644
--- a/lib/leap_cli/commands/shell.rb
+++ b/lib/leap_cli/commands/shell.rb
@@ -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
diff --git a/lib/leap_cli/commands/util.rb b/lib/leap_cli/commands/util.rb
index 164ce0d..c1da570 100644
--- a/lib/leap_cli/commands/util.rb
+++ b/lib/leap_cli/commands/util.rb
@@ -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
index 0000000..587e5e1
--- /dev/null
+++ b/lib/leap_cli/commands/vagrant.rb
@@ -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
diff --git a/lib/leap_cli/config/manager.rb b/lib/leap_cli/config/manager.rb
index 88f21be..00b4ec5 100644
--- a/lib/leap_cli/config/manager.rb
+++ b/lib/leap_cli/config/manager.rb
@@ -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
diff --git a/lib/leap_cli/config/object.rb b/lib/leap_cli/config/object.rb
index ad32f54..731f3ff 100644
--- a/lib/leap_cli/config/object.rb
+++ b/lib/leap_cli/config/object.rb
@@ -145,6 +145,20 @@ module LeapCli
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
##
diff --git a/lib/leap_cli/config/object_list.rb b/lib/leap_cli/config/object_list.rb
index 708afc1..b0839ca 100644
--- a/lib/leap_cli/config/object_list.rb
+++ b/lib/leap_cli/config/object_list.rb
@@ -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
diff --git a/lib/leap_cli/path.rb b/lib/leap_cli/path.rb
index 6d68546..48b0d11 100644
--- a/lib/leap_cli/path.rb
+++ b/lib/leap_cli/path.rb
@@ -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
index 0000000..118a65e
--- /dev/null
+++ b/lib/leap_cli/util/remote_command.rb
@@ -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
diff --git a/test/provider/common.json b/test/provider/common.json
index 7504e86..85a93cb 100644
--- a/test/provider/common.json
+++ b/test/provider/common.json
@@ -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?"
}
diff --git a/test/provider/nodes/couch1.json b/test/provider/nodes/couch1.json
index fe5d7e5..d246b99 100644
--- a/test/provider/nodes/couch1.json
+++ b/test/provider/nodes/couch1.json
@@ -1,4 +1,4 @@
{
"services": "couchdb",
- "ip_address": "245.2.45.42"
+ "ip_address": "10.5.5.2"
} \ No newline at end of file
diff --git a/test/provider/nodes/vpn1.json b/test/provider/nodes/vpn1.json
index 1c58a1b..5115cb2 100644
--- a/test/provider/nodes/vpn1.json
+++ b/test/provider/nodes/vpn1.json
@@ -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",
diff --git a/test/provider/provider.json b/test/provider/provider.json
index e65eebe..6e7618f 100644
--- a/test/provider/provider.json
+++ b/test/provider/provider.json
@@ -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
index 0000000..905756d
--- /dev/null
+++ b/vendor/vagrant_ssh_keys/README
@@ -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
index 0000000..7d6a083
--- /dev/null
+++ b/vendor/vagrant_ssh_keys/vagrant.key
@@ -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
index 0000000..18a9c00
--- /dev/null
+++ b/vendor/vagrant_ssh_keys/vagrant.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key