From 1a2a8b49c0d9bce90acf71fa22db823568956387 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 5 Aug 2012 12:41:05 -0700 Subject: [PATCH] Provider API to return SSH info, must implement `ssh_info`. Since SSH is such a critical part of Vagrant, each provider must implement a method that returns the proper way to SSH into the machine. --- lib/vagrant/machine.rb | 54 +++++++++++++++ lib/vagrant/plugin/v1/provider.rb | 25 +++++++ test/unit/vagrant/machine_test.rb | 69 +++++++++++++++++++- test/unit/vagrant/plugin/v1/provider_test.rb | 8 +++ 4 files changed, 155 insertions(+), 1 deletion(-) diff --git a/lib/vagrant/machine.rb b/lib/vagrant/machine.rb index 294e4983b..600f7ad05 100644 --- a/lib/vagrant/machine.rb +++ b/lib/vagrant/machine.rb @@ -116,6 +116,60 @@ module Vagrant @id = value end + # This returns the SSH info for accessing this machine. This SSH info + # is queried from the underlying provider. This method returns `nil` if + # the machine is not ready for SSH communication. + # + # The structure of the resulting hash is guaranteed to contain the + # following structure, although it may return other keys as well + # not documented here: + # + # { + # :host => "1.2.3.4", + # :port => "22", + # :username => "mitchellh", + # :private_key_path => "/path/to/my/key" + # } + # + # Note that Vagrant makes no guarantee that this info works or is + # correct. This is simply the data that the provider gives us or that + # is configured via a Vagrantfile. It is still possible after this + # point when attempting to connect via SSH to get authentication + # errors. + # + # @return [Hash] SSH information. + def ssh_info + # First, ask the provider for their information. If the provider + # returns nil, then the machine is simply not ready for SSH, and + # we return nil as well. + info = @provider.ssh_info + return nil if info.nil? + + # Delete out the nil entries. + info.dup.each do |key, value| + info.delete(key) if value.nil? + end + + # Next, we default some fields if they weren't given to us by + # the provider. + info[:host] = @config.ssh.host if @config.ssh.host + info[:port] = @config.ssh.port if @config.ssh.port + info[:username] = @config.ssh.username if @config.ssh.username + + # We also set some fields that are purely controlled by Varant + info[:forward_agent] = @config.ssh.forward_agent + info[:forward_x11] = @config.ssh.forward_x11 + + # Set the private key path. If a specific private key is given in + # the Vagrantfile we set that. Otherwise, we use the default (insecure) + # private key, but only if the provider didn't give us one. + info[:private_key_path] = @config.ssh.private_key_path if @config.ssh.private_key_path + info[:private_key_path] = @env.default_private_key_path if !info[:private_key_path] + + # Return the final compiled SSH info data + info + end + # Returns the state of this machine. The state is queried from the # backing provider, so it can be any arbitrary symbol. # diff --git a/lib/vagrant/plugin/v1/provider.rb b/lib/vagrant/plugin/v1/provider.rb index 09fe39962..513b4c2d8 100644 --- a/lib/vagrant/plugin/v1/provider.rb +++ b/lib/vagrant/plugin/v1/provider.rb @@ -21,6 +21,31 @@ module Vagrant nil end + # This should return a hash of information that explains how to + # SSH into the machine. If the machine is not at a point where + # SSH is even possible, then `nil` should be returned. + # + # The general structure of this returned hash should be the + # following: + # + # { + # :host => "1.2.3.4", + # :port => "22", + # :username => "mitchellh", + # :private_key_path => "/path/to/my/key" + # } + # + # **Note:** Vagrant only supports private key based authentication, + # mainly for the reason that there is no easy way to exec into an + # `ssh` prompt with a password, whereas we can pass a private key + # via commandline. + # + # @return [Hash] SSH information. For the structure of this hash + # read the accompanying documentation for this method. + def ssh_info + nil + end + # This should return the state of the machine within this provider. # The state can be any symbol. # diff --git a/test/unit/vagrant/machine_test.rb b/test/unit/vagrant/machine_test.rb index 2c6d6a09c..a85166437 100644 --- a/test/unit/vagrant/machine_test.rb +++ b/test/unit/vagrant/machine_test.rb @@ -11,7 +11,7 @@ describe Vagrant::Machine do obj end let(:box) { Object.new } - let(:config) { Object.new } + let(:config) { env.config.global } let(:env) do # We need to create a Vagrantfile so that this test environment # has a proper root path @@ -175,6 +175,73 @@ describe Vagrant::Machine do end end + describe "ssh info" do + describe "with the provider returning nil" do + it "should return nil if the provider returns nil" do + provider.should_receive(:ssh_info).and_return(nil) + instance.ssh_info.should be_nil + end + end + + describe "with the provider returning data" do + let(:provider_ssh_info) { {} } + + before(:each) do + provider.should_receive(:ssh_info).and_return(provider_ssh_info) + end + + [:host, :port, :username].each do |type| + it "should return the provider data if not configured in Vagrantfile" do + provider_ssh_info[type] = "foo" + instance.config.ssh.send("#{type}=", nil) + + instance.ssh_info[type].should == "foo" + end + + it "should return the Vagrantfile value over the provider data if given" do + provider_ssh_info[type] = "foo" + instance.config.ssh.send("#{type}=", "bar") + + instance.ssh_info[type].should == "bar" + end + end + + it "should set the configured forward agent settings" do + provider_ssh_info[:forward_agent] = true + instance.config.ssh.forward_agent = false + + instance.ssh_info[:forward_agent].should == false + end + + it "should set the configured forward X11 settings" do + provider_ssh_info[:forward_x11] = true + instance.config.ssh.forward_x11 = false + + instance.ssh_info[:forward_x11].should == false + end + + it "should return the provider private key if given" do + provider_ssh_info[:private_key_path] = "foo" + + instance.ssh_info[:private_key_path].should == "foo" + end + + it "should return the configured SSH key path if set" do + provider_ssh_info[:private_key_path] = "foo" + instance.config.ssh.private_key_path = "bar" + + instance.ssh_info[:private_key_path].should == "bar" + end + + it "should return the default private key path if provider and config doesn't have one" do + provider_ssh_info[:private_key_path] = nil + instance.config.ssh.private_key_path = nil + + instance.ssh_info[:private_key_path].should == instance.env.default_private_key_path + end + end + end + describe "state" do it "should query state from the provider" do state = :running diff --git a/test/unit/vagrant/plugin/v1/provider_test.rb b/test/unit/vagrant/plugin/v1/provider_test.rb index b92bd3385..2950d9bbf 100644 --- a/test/unit/vagrant/plugin/v1/provider_test.rb +++ b/test/unit/vagrant/plugin/v1/provider_test.rb @@ -7,4 +7,12 @@ describe Vagrant::Plugin::V1::Provider do it "should return nil by default for actions" do instance.action(:whatever).should be_nil end + + it "should return nil by default for ssh info" do + instance.ssh_info.should be_nil + end + + it "should return nil by default for state" do + instance.state.should be_nil + end end