From 063f60e593b6c13db3cdeb0c193586432e7dfc13 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Tue, 24 Nov 2015 11:08:30 -0500 Subject: [PATCH] Add `vagrant port` command --- plugins/commands/port/command.rb | 71 ++++++++++++++ plugins/commands/port/plugin.rb | 17 ++++ .../plugins/commands/port/command_test.rb | 98 +++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 plugins/commands/port/command.rb create mode 100644 plugins/commands/port/plugin.rb create mode 100644 test/unit/plugins/commands/port/command_test.rb diff --git a/plugins/commands/port/command.rb b/plugins/commands/port/command.rb new file mode 100644 index 000000000..276b6baba --- /dev/null +++ b/plugins/commands/port/command.rb @@ -0,0 +1,71 @@ +require "optparse" + +module VagrantPlugins + module CommandPort + class Command < Vagrant.plugin("2", :command) + def self.synopsis + "displays information about guest port mappings" + end + + # @todo support multiple strategies if requested by the community + def execute + opts = OptionParser.new do |o| + o.banner = "Usage: vagrant port [options] [name]" + o.separator "" + o.separator "Options:" + o.separator "" + + o.on("--machine-readable", "Display machine-readable output") + end + + # Parse the options + argv = parse_options(opts) + return if !argv + + @logger.debug("Port command: #{argv.inspect}") + with_target_vms(argv, single_target: true) do |vm| + vm.action_raw(:config_validate, + Vagrant::Action::Builtin::ConfigValidate) + + if vm.state.id != :running + @env.ui.error "not running - make this a better error or use the middleware" + return 1 + end + + # This only works for vbox? should it be everywhere? + # vm.action_raw(:check_running, + # Vagrant::Action::Builtin::CheckRunning) + + if !vm.provider.capability?(:forwarded_ports) + @env.ui.error <<-EOH.strip +The #{vm.provider_name} provider does not support listing forwarded ports. This +is most likely a limitation of the provider and not a bug in Vagrant. If you +believe this is a bug in Vagrant, please search existing issues before opening +a new one. +EOH + return 1 + end + + ports = vm.provider.capability(:forwarded_ports) + + if ports.empty? + @env.ui.info("The machine has no configured forwarded ports") + return 0 + end + + @env.ui.info <<-EOH +The forwarded ports for the machine are listed below. Please note that these +values may differ from values configured in the Vagrantfile if the provider +supports automatic port collision detection and resolution. +EOH + ports.each do |guest, host| + @env.ui.info("#{guest.to_s.rjust(6)} (guest) => #{host} (host)") + @env.ui.machine("forwarded_port", guest, host, target: vm.name.to_s) + end + end + + 0 + end + end + end +end diff --git a/plugins/commands/port/plugin.rb b/plugins/commands/port/plugin.rb new file mode 100644 index 000000000..be1572afd --- /dev/null +++ b/plugins/commands/port/plugin.rb @@ -0,0 +1,17 @@ +require "vagrant" + +module VagrantPlugins + module CommandPort + class Plugin < Vagrant.plugin("2") + name "port command" + description <<-DESC + The `port` command displays guest port mappings. + DESC + + command("port") do + require File.expand_path("../command", __FILE__) + Command + end + end + end +end diff --git a/test/unit/plugins/commands/port/command_test.rb b/test/unit/plugins/commands/port/command_test.rb new file mode 100644 index 000000000..7bd3dee4e --- /dev/null +++ b/test/unit/plugins/commands/port/command_test.rb @@ -0,0 +1,98 @@ +require File.expand_path("../../../../base", __FILE__) + +require Vagrant.source_root.join("plugins/commands/port/command") + +describe VagrantPlugins::CommandPort::Command do + include_context "unit" + include_context "command plugin helpers" + + let(:iso_env) { isolated_environment } + let(:env) do + iso_env.vagrantfile(<<-VF) + Vagrant.configure("2") do |config| + config.vm.box = "hashicorp/precise64" + end + VF + iso_env.create_vagrant_env + end + + let(:argv) { [] } + let(:pushes) { {} } + let(:state) { double(:state, id: :running) } + + let(:machine) { env.machine(env.machine_names[0], :dummy) } + + subject { described_class.new(argv, env) } + + before do + allow(machine).to receive(:state).and_return(state) + allow(subject).to receive(:with_target_vms) { |&block| block.call(machine) } + end + + describe "#execute" do + it "validates the configuration" do + iso_env.vagrantfile <<-EOH + Vagrant.configure("2") do |config| + config.vm.box = "hashicorp/precise64" + + config.push.define "noop" do |push| + push.bad = "ham" + end + end + EOH + + subject = described_class.new(argv, iso_env.create_vagrant_env) + + expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| + expect(err.message).to include("The following settings shouldn't exist: bad") + } + end + + it "ensures the vm is running" do + allow(state).to receive(:id).and_return(:stopped) + expect(env.ui).to receive(:error).with { |message, _| + expect(message).to include("make this a better error") + } + + expect(subject.execute).to eq(1) + end + + it "shows a friendly error when the capability is not supported" do + allow(machine.provider).to receive(:capability?).and_return(false) + expect(env.ui).to receive(:error).with { |message, _| + expect(message).to include("does not support listing forwarded ports") + } + + expect(subject.execute).to eq(1) + end + + it "returns a friendly message when there are no forwarded ports" do + allow(machine.provider).to receive(:capability?).and_return(true) + allow(machine.provider).to receive(:capability).with(:forwarded_ports) + .and_return([]) + + expect(env.ui).to receive(:info).with { |message, _| + expect(message).to include("has no configured forwarded ports") + } + + expect(subject.execute).to eq(0) + end + + it "returns the list of ports" do + allow(machine.provider).to receive(:capability?).and_return(true) + allow(machine.provider).to receive(:capability).with(:forwarded_ports) + .and_return([[2222,22], [1111,11]]) + + output = "" + allow(env.ui).to receive(:info) do |data| + output << data + end + + expect(subject.execute).to eq(0) + + expect(output).to include("forwarded ports for the machine") + expect(output).to include("2222 (guest) => 22 (host)") + expect(output).to include("1111 (guest) => 11 (host)") + end + end +end