vagrant-skytap 0.2.10 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/vagrant-skytap/action/check_created.rb +43 -0
- data/lib/vagrant-skytap/action/check_running.rb +43 -0
- data/lib/vagrant-skytap/action/get_host_vm.rb +52 -0
- data/lib/vagrant-skytap/action/prepare_nfs_settings.rb +22 -5
- data/lib/vagrant-skytap/action.rb +11 -18
- data/lib/vagrant-skytap/api/connectable.rb +50 -0
- data/lib/vagrant-skytap/api/environment.rb +10 -5
- data/lib/vagrant-skytap/api/interface.rb +9 -0
- data/lib/vagrant-skytap/api/network.rb +60 -1
- data/lib/vagrant-skytap/api/public_ip.rb +7 -75
- data/lib/vagrant-skytap/api/publish_set.rb +4 -0
- data/lib/vagrant-skytap/api/published_service.rb +7 -71
- data/lib/vagrant-skytap/api/resource.rb +25 -3
- data/lib/vagrant-skytap/api/tunnel.rb +69 -0
- data/lib/vagrant-skytap/api/vm.rb +10 -2
- data/lib/vagrant-skytap/api/vpn.rb +2 -106
- data/lib/vagrant-skytap/cap/host_metadata.rb +45 -0
- data/lib/vagrant-skytap/connection/public_ip_choice.rb +94 -0
- data/lib/vagrant-skytap/connection/published_service_choice.rb +100 -0
- data/lib/vagrant-skytap/connection/tunnel_choice.rb +118 -0
- data/lib/vagrant-skytap/connection/vpn_choice.rb +132 -0
- data/lib/vagrant-skytap/connection.rb +123 -0
- data/lib/vagrant-skytap/errors.rb +4 -0
- data/lib/vagrant-skytap/plugin.rb +5 -0
- data/lib/vagrant-skytap/setup_helper.rb +34 -8
- data/lib/vagrant-skytap/version.rb +1 -1
- data/locales/en.yml +46 -0
- data/spec/unit/actions/prepare_nfs_settings_spec.rb +63 -16
- data/spec/unit/cap/host_metadata_spec.rb +43 -0
- data/spec/unit/connections/public_ip_choice_spec.rb +57 -0
- data/spec/unit/connections/published_service_choice_spec.rb +79 -0
- data/spec/unit/connections/tunnel_choice_spec.rb +124 -0
- data/spec/unit/connections/vpn_choice_spec.rb +109 -0
- data/spec/unit/interface_spec.rb +53 -0
- data/spec/unit/network_spec.rb +123 -0
- data/spec/unit/setup_helper_spec.rb +59 -19
- data/spec/unit/support/api_responses/tunnel1.json +7 -0
- data/spec/unit/support/api_responses/vm1.json +12 -1
- data/spec/unit/support/shared/rest_api_context.rb +1 -0
- data/spec/unit/tunnel_spec.rb +62 -0
- data/spec/unit/vm_spec.rb +53 -60
- metadata +22 -2
@@ -0,0 +1,118 @@
|
|
1
|
+
# Copyright (c) 2014-2016 Skytap, Inc.
|
2
|
+
#
|
3
|
+
# The MIT License (MIT)
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'vagrant-skytap/connection'
|
24
|
+
|
25
|
+
# An ICNR tunnel connects networks in two different Skytap environments.
|
26
|
+
# In the case where the host is a Skytap VM, a tunnel is the most convenient
|
27
|
+
# way of establishing communication with the guest VM.
|
28
|
+
|
29
|
+
module VagrantPlugins
|
30
|
+
module Skytap
|
31
|
+
module Connection
|
32
|
+
class TunnelChoice < Choice
|
33
|
+
attr_reader :host_network, :guest_network
|
34
|
+
|
35
|
+
def initialize(env, host_network, iface)
|
36
|
+
@env = env
|
37
|
+
@iface = iface
|
38
|
+
@host_network = host_network
|
39
|
+
@guest_network = iface.network
|
40
|
+
@execution = TunnelExecution.make(env, iface, host_network)
|
41
|
+
end
|
42
|
+
|
43
|
+
def choose
|
44
|
+
execution.execute
|
45
|
+
@iface = iface.vm.reload.get_interface_by_id(iface.id)
|
46
|
+
@host_network = host_network.environment.reload.networks.find{|n| n.id == host_network.id}
|
47
|
+
nat_address = iface.nat_address_for_network(host_network)
|
48
|
+
[nat_address, DEFAULT_PORT]
|
49
|
+
end
|
50
|
+
|
51
|
+
def valid?
|
52
|
+
@validation_error_message = nil
|
53
|
+
|
54
|
+
unless host_network.tunnelable?
|
55
|
+
@validation_error_message = I18n.t("vagrant_skytap.connections.tunnel.errors.host_network_not_tunnelable")
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
unless host_network.nat_enabled?
|
60
|
+
@validation_error_message = I18n.t("vagrant_skytap.connections.tunnel.errors.host_network_nat_disabled")
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
|
64
|
+
unless guest_network.nat_enabled?
|
65
|
+
if guest_network.subnet.overlaps?(host_network.subnet)
|
66
|
+
@validation_error_message = I18n.t("vagrant_skytap.connections.tunnel.errors.guest_network_overlaps")
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
class TunnelExecution < Execution
|
75
|
+
attr_reader :host_network, :guest_network
|
76
|
+
|
77
|
+
def self.make(env, iface, host_network)
|
78
|
+
if host_network.try(:connected_to_network?, iface.network)
|
79
|
+
UseExecution.new(env, iface, host_network)
|
80
|
+
else
|
81
|
+
CreateAndUseExecution.new(env, iface, host_network)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def initialize(env, iface, host_network)
|
86
|
+
super
|
87
|
+
@host_network = host_network
|
88
|
+
@guest_network = iface.network
|
89
|
+
end
|
90
|
+
|
91
|
+
def message
|
92
|
+
"#{verb}: #{host_network.name}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class UseExecution < TunnelExecution
|
97
|
+
def verb
|
98
|
+
I18n.t("vagrant_skytap.connections.tunnel.verb_use")
|
99
|
+
end
|
100
|
+
|
101
|
+
def execute
|
102
|
+
# No-op
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class CreateAndUseExecution < TunnelExecution
|
107
|
+
def verb
|
108
|
+
I18n.t("vagrant_skytap.connections.tunnel.verb_create_and_use")
|
109
|
+
end
|
110
|
+
|
111
|
+
def execute
|
112
|
+
guest_network.connect_to_network(host_network)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# Copyright (c) 2014-2016 Skytap, Inc.
|
2
|
+
#
|
3
|
+
# The MIT License (MIT)
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'vagrant-skytap/connection'
|
24
|
+
require 'vagrant-skytap/api/vpn_attachment'
|
25
|
+
|
26
|
+
module VagrantPlugins
|
27
|
+
module Skytap
|
28
|
+
module Connection
|
29
|
+
class VpnChoice < Choice
|
30
|
+
attr_reader :vm, :vpn, :attachment
|
31
|
+
|
32
|
+
def initialize(env, vpn, vm)
|
33
|
+
@env = env
|
34
|
+
@vpn = vpn
|
35
|
+
@vm = vm
|
36
|
+
@iface = select_interface(vm, vpn)
|
37
|
+
@execution = VPNAttachmentExecution.make(env, iface, vpn)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Finds an interface on the guest VM which is connected to a network
|
41
|
+
# which lies inside the VPN's subnet. If the VPN is NAT enabled, this
|
42
|
+
# method simply returns the first interface.
|
43
|
+
#
|
44
|
+
# @param [API::Vm] vm The guest VM
|
45
|
+
# @param [API::Vpn] vpn The VPN this VpnChoice will connect to
|
46
|
+
# @return [API::Interface] An interface connected to a suitable network
|
47
|
+
def select_interface(vm, vpn)
|
48
|
+
vm.interfaces.select(&:network).tap do |ifaces|
|
49
|
+
unless vpn.nat_enabled?
|
50
|
+
ifaces.select! {|i| vpn.subsumes?(i.network) }
|
51
|
+
end
|
52
|
+
end.first
|
53
|
+
end
|
54
|
+
|
55
|
+
def choose
|
56
|
+
execution.execute
|
57
|
+
@iface = vm.reload.get_interface_by_id(iface.id)
|
58
|
+
host = iface.address_for(vpn)
|
59
|
+
[host, DEFAULT_PORT]
|
60
|
+
end
|
61
|
+
|
62
|
+
# To communicate with the guest VM over a VPN, the guest's network
|
63
|
+
# must be both attached and connected to the VPN. The various
|
64
|
+
# subclasses perform different REST calls depending on which of these
|
65
|
+
# conditions are already met.
|
66
|
+
class VPNAttachmentExecution < Execution
|
67
|
+
attr_reader :vpn, :attachment
|
68
|
+
|
69
|
+
def self.make(env, iface, vpn)
|
70
|
+
attachment = iface.attachment_for(vpn)
|
71
|
+
|
72
|
+
if attachment.try(:connected?)
|
73
|
+
UseExecution.new(env, iface, vpn, attachment)
|
74
|
+
elsif attachment
|
75
|
+
ConnectAndUseExecution.new(env, iface, vpn, attachment)
|
76
|
+
else
|
77
|
+
AttachConnectAndUseExecution.new(env, iface, vpn)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize(env, iface, vpn, attachment=nil)
|
82
|
+
super
|
83
|
+
@vpn = vpn
|
84
|
+
@attachment = attachment
|
85
|
+
end
|
86
|
+
|
87
|
+
def message
|
88
|
+
"#{verb}: #{vpn.name}".tap do |ret|
|
89
|
+
if vpn.nat_enabled?
|
90
|
+
ret << " " << I18n.t("vagrant_skytap.connections.vpn_attachment.nat_enabled")
|
91
|
+
else
|
92
|
+
ret << " " << I18n.t("vagrant_skytap.connections.vpn_attachment.local_subnet",
|
93
|
+
local_subnet: vpn.local_subnet)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class UseExecution < VPNAttachmentExecution
|
100
|
+
def verb
|
101
|
+
I18n.t("vagrant_skytap.connections.vpn_attachment.verb_use")
|
102
|
+
end
|
103
|
+
|
104
|
+
def execute
|
105
|
+
# No-op
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class ConnectAndUseExecution < VPNAttachmentExecution
|
110
|
+
def verb
|
111
|
+
I18n.t("vagrant_skytap.connections.vpn_attachment.verb_connect")
|
112
|
+
end
|
113
|
+
|
114
|
+
def execute
|
115
|
+
attachment.connect!
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class AttachConnectAndUseExecution < VPNAttachmentExecution
|
120
|
+
def verb
|
121
|
+
I18n.t("vagrant_skytap.connections.vpn_attachment.verb_attach")
|
122
|
+
end
|
123
|
+
|
124
|
+
def execute
|
125
|
+
@attachment = API::VpnAttachment.create(iface.network, vpn, env)
|
126
|
+
@attachment.connect!
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Copyright (c) 2014-2016 Skytap, Inc.
|
2
|
+
#
|
3
|
+
# The MIT License (MIT)
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'net/ssh/transport/session'
|
24
|
+
|
25
|
+
# Encapsulates a chooseable option for establishing communication with the
|
26
|
+
# guest VM over SSH/WinRM. All valid options (e.g. using specific VPNs,
|
27
|
+
# creating a published service, etc.) are collected as Choices and
|
28
|
+
# presented to the user.
|
29
|
+
|
30
|
+
module VagrantPlugins
|
31
|
+
module Skytap
|
32
|
+
module Connection
|
33
|
+
DEFAULT_PORT = Net::SSH::Transport::Session::DEFAULT_PORT
|
34
|
+
|
35
|
+
# A Choice represents the potential for establishing a connection to the
|
36
|
+
# guest VM via a specific resource.
|
37
|
+
class Choice
|
38
|
+
attr_reader :env, :iface, :execution, :validation_error_message
|
39
|
+
|
40
|
+
# Thia method should be overridden to call #make on the
|
41
|
+
# [Connection::Execution] subclass for this particular resource type.
|
42
|
+
# The execution holds all the information needed to establish the
|
43
|
+
# connection.
|
44
|
+
def initialize(*args)
|
45
|
+
@env = args.first
|
46
|
+
end
|
47
|
+
|
48
|
+
# Invokes the execution, and determines the IP address and port for
|
49
|
+
# communicating with the guest VM.
|
50
|
+
#
|
51
|
+
# @return [Array] Tuple of [String], [Integer] The IP address and port
|
52
|
+
# for communicating with the guest VM.
|
53
|
+
def choose
|
54
|
+
raise NotImplementedError.new('Must override')
|
55
|
+
end
|
56
|
+
|
57
|
+
# Override this method to add any validation logic needed by a specific
|
58
|
+
# resource type. For example, [Connection::TunnelChoice] must check for
|
59
|
+
# subnet overlaps. If this method returns false, this particular choice
|
60
|
+
# should not be offered to the user. For some resource types, it may
|
61
|
+
# be useful to set @validation_error_message on this choice when
|
62
|
+
# returning false.
|
63
|
+
#
|
64
|
+
# @return [Boolean]
|
65
|
+
def valid?
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
execution.message
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# An Execution implements a strategy for establishing a connection
|
75
|
+
# to the guest VM using a given resource. A resource may have more
|
76
|
+
# than one strategy, depending on the initial state of the connection.
|
77
|
+
# (When the guest VM is first created, no connection will exist.)
|
78
|
+
class Execution
|
79
|
+
attr_reader :env, :iface
|
80
|
+
|
81
|
+
def initialize(*args)
|
82
|
+
@env, @iface, _ = args
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates an execution object which will perform the correct actions
|
86
|
+
# to establish a connection via a specific connectable resource. This
|
87
|
+
# method should be overridden to return an object of the correct
|
88
|
+
# execution subclass given the resource's initial state. For example,
|
89
|
+
# if the guest's network is already attached to this VPN, but
|
90
|
+
# disconnected, this method would return a ConnectAndUseExecution.
|
91
|
+
#
|
92
|
+
# @return [Connection::Execution]
|
93
|
+
def make(*args)
|
94
|
+
raise NotImplementedError.new('Must override')
|
95
|
+
end
|
96
|
+
|
97
|
+
# Performs the API calls which establish the connection for
|
98
|
+
# communicating with the guest VM. If the connection is already
|
99
|
+
# established, as in the case of a guest VM which already exists,
|
100
|
+
# this may be a no-op.
|
101
|
+
def execute
|
102
|
+
raise NotImplementedError.new('Must override')
|
103
|
+
end
|
104
|
+
|
105
|
+
# The name of the action(s) performed by this execution subclass,
|
106
|
+
# e.g. "Connect and use".
|
107
|
+
#
|
108
|
+
# @return [String]
|
109
|
+
def verb
|
110
|
+
raise NotImplementedError.new('Must override')
|
111
|
+
end
|
112
|
+
|
113
|
+
# The description of the actions to be taken when this choice is
|
114
|
+
# executed, e.g. "Connect to and use VPN 1".
|
115
|
+
#
|
116
|
+
# @return [String]
|
117
|
+
def message
|
118
|
+
verb
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -89,6 +89,10 @@ module VagrantPlugins
|
|
89
89
|
error_key(:no_connection_options)
|
90
90
|
end
|
91
91
|
|
92
|
+
class NoSkytapConnectionOptions < VagrantSkytapError
|
93
|
+
error_key(:no_skytap_connection_options)
|
94
|
+
end
|
95
|
+
|
92
96
|
class FeatureNotSupportedForHostOs < VagrantSkytapError
|
93
97
|
error_key(:feature_not_supported_for_host_os)
|
94
98
|
end
|
@@ -71,6 +71,11 @@ module VagrantPlugins
|
|
71
71
|
Cap::PublicAddress
|
72
72
|
end
|
73
73
|
|
74
|
+
provider_capability(:skytap, :host_metadata) do
|
75
|
+
require_relative "cap/host_metadata"
|
76
|
+
Cap::HostMetadata
|
77
|
+
end
|
78
|
+
|
74
79
|
%w[start_ssh_tunnel kill_ssh_tunnel clear_forwarded_ports read_forwarded_ports read_used_ports].each do |cap|
|
75
80
|
host_capability("bsd", cap) do
|
76
81
|
require_relative "hosts/bsd/cap/ssh_tunnel"
|
@@ -29,7 +29,7 @@ require 'net/ssh/transport/session'
|
|
29
29
|
module VagrantPlugins
|
30
30
|
module Skytap
|
31
31
|
class SetupHelper
|
32
|
-
attr_reader :env, :environment, :machine, :provider_config
|
32
|
+
attr_reader :env, :environment, :host_vm, :machine, :provider_config
|
33
33
|
attr_reader :username, :password, :host, :port
|
34
34
|
|
35
35
|
def self.run!(env, environment)
|
@@ -40,6 +40,7 @@ module VagrantPlugins
|
|
40
40
|
@env = env
|
41
41
|
@logger = Log4r::Logger.new("vagrant_skytap::setup_helper")
|
42
42
|
@environment = environment
|
43
|
+
@host_vm = env[:vagrant_host_vm]
|
43
44
|
@machine = env[:machine]
|
44
45
|
@provider_config = env[:machine].provider_config
|
45
46
|
@username = @machine.config.ssh.username
|
@@ -54,6 +55,10 @@ module VagrantPlugins
|
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
58
|
+
def running_in_skytap_vm?
|
59
|
+
!!host_vm
|
60
|
+
end
|
61
|
+
|
57
62
|
def run!
|
58
63
|
ask_routing
|
59
64
|
ask_credentials
|
@@ -94,30 +99,46 @@ module VagrantPlugins
|
|
94
99
|
end
|
95
100
|
|
96
101
|
def ask_routing
|
97
|
-
@logger.debug("ask_routing")
|
98
102
|
unless host && port
|
99
103
|
iface = current_vm.interfaces.first
|
100
|
-
choices = connection_choices(iface)
|
104
|
+
choices = connection_choices(iface)
|
105
|
+
|
106
|
+
if running_in_skytap_vm?
|
107
|
+
unless choices.any?(&:valid?)
|
108
|
+
raise Errors::NoSkytapConnectionOptions, message: choices.collect(&:validation_error_message).first
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
choices.select!(&:valid?)
|
101
113
|
raise Errors::NoConnectionOptions unless choices.present?
|
102
114
|
|
103
|
-
|
115
|
+
choice = nil
|
116
|
+
if !running_in_skytap_vm? && vpn_url = @provider_config.vpn_url
|
104
117
|
choice = choices.detect do |choice|
|
105
118
|
choice.vpn && vpn_url.include?(choice.vpn.id)
|
106
119
|
end
|
107
120
|
raise Errors::DoesNotExist, object_name: vpn_url unless choice
|
108
|
-
|
121
|
+
elsif choices.count == 1
|
122
|
+
choice = choices.first
|
123
|
+
env[:ui].info("Using only available connection option: #{choice}")
|
109
124
|
else
|
110
125
|
question = "How do you want to connect to machine '#{@machine.name}'?"
|
111
|
-
ask_from_list(question, choices, 0) do |i,
|
112
|
-
|
126
|
+
ask_from_list(question, choices, 0) do |i, _choice|
|
127
|
+
choice = _choice
|
113
128
|
end
|
114
129
|
end
|
130
|
+
@host, @port = choice.choose
|
115
131
|
end
|
132
|
+
@logger.debug("ask_routing returning guest VM address and port: #{@host}:#{@port}")
|
116
133
|
[@host, @port]
|
117
134
|
end
|
118
135
|
|
119
136
|
def connection_choices(iface)
|
120
|
-
|
137
|
+
if running_in_skytap_vm?
|
138
|
+
tunnel_choices(iface)
|
139
|
+
else
|
140
|
+
vpn_choices(iface)
|
141
|
+
end
|
121
142
|
end
|
122
143
|
|
123
144
|
def vpn_choices(iface)
|
@@ -137,6 +158,11 @@ module VagrantPlugins
|
|
137
158
|
iface.published_service_choices
|
138
159
|
end
|
139
160
|
|
161
|
+
def tunnel_choices(guest_iface)
|
162
|
+
candidates = host_vm.interfaces.collect(&:network)
|
163
|
+
candidates.collect {|network| network.choice_for_setup(guest_iface) }
|
164
|
+
end
|
165
|
+
|
140
166
|
def vpns
|
141
167
|
VagrantPlugins::Skytap::API::Vpn.all(env, query: {region: environment.region})
|
142
168
|
end
|
data/locales/en.yml
CHANGED
@@ -100,6 +100,11 @@ en:
|
|
100
100
|
There were no available options for connecting to the VM.
|
101
101
|
Currently, the provider supports connections over VPN only.
|
102
102
|
|
103
|
+
no_skytap_connection_options: |-
|
104
|
+
Could not establish a connection between the host and guest VMs.
|
105
|
+
%{message}
|
106
|
+
Please correct the problem and try again.
|
107
|
+
|
103
108
|
bad_vm_url: |-
|
104
109
|
The specified vm_url was invalid: %{url}
|
105
110
|
|
@@ -143,6 +148,47 @@ en:
|
|
143
148
|
long_busy: |-
|
144
149
|
The Skytap instance is busy. Wait for the current operation to complete.
|
145
150
|
|
151
|
+
connections:
|
152
|
+
vpn_attachment:
|
153
|
+
verb_use: |-
|
154
|
+
Use VPN
|
155
|
+
verb_connect: |-
|
156
|
+
Connect to and use VPN
|
157
|
+
verb_attach: |-
|
158
|
+
Attach to and use VPN
|
159
|
+
nat_enabled: |-
|
160
|
+
(NAT-enabled)
|
161
|
+
local_subnet: |-
|
162
|
+
(local subnet: %{local_subnet})
|
163
|
+
published_service:
|
164
|
+
verb_use: |-
|
165
|
+
Use published service
|
166
|
+
verb_create_and_use: |-
|
167
|
+
Create and use published service
|
168
|
+
public_ip:
|
169
|
+
verb_use: |-
|
170
|
+
Use public IP
|
171
|
+
verb_attach: |-
|
172
|
+
Attach and use public IP
|
173
|
+
deployed: |-
|
174
|
+
(attached and deployed to another VM)
|
175
|
+
attached: |-
|
176
|
+
(attached to another VM)
|
177
|
+
tunnel:
|
178
|
+
verb_use: |-
|
179
|
+
Use existing tunnel to host network
|
180
|
+
verb_create_and_use: |-
|
181
|
+
Create and use tunnel to host network
|
182
|
+
errors:
|
183
|
+
host_network_not_tunnelable: |-
|
184
|
+
The host's network must be tunnelable.
|
185
|
+
host_network_nat_disabled: |-
|
186
|
+
The host's network must have NAT enabled.
|
187
|
+
guest_network_overlaps: |-
|
188
|
+
The guest's network subnet overlaps with the host's network.
|
189
|
+
(This can be resolved by changing network subnets, or by
|
190
|
+
enabling NAT on the guest network.)
|
191
|
+
|
146
192
|
commands:
|
147
193
|
publish_urls:
|
148
194
|
list: |-
|
@@ -26,8 +26,12 @@ require "vagrant-skytap/action/prepare_nfs_settings"
|
|
26
26
|
describe VagrantPlugins::Skytap::Action::PrepareNFSSettings do
|
27
27
|
include_context "skytap"
|
28
28
|
|
29
|
-
let(:
|
30
|
-
let(:
|
29
|
+
let(:json_path) { File.join(File.expand_path('..', __FILE__), 'support', 'api_responses') }
|
30
|
+
let(:vm1_attrs) { read_json(json_path, 'vm1.json') }
|
31
|
+
|
32
|
+
let(:app) { lambda { |env| } }
|
33
|
+
let(:host_vm) { nil }
|
34
|
+
let(:env) { { machine: machine, ui: ui, host_vm: host_vm} }
|
31
35
|
|
32
36
|
let(:machine) { double(:machine, config: config, provider_config: provider_config, ssh_info: {host: guest_ip}) }
|
33
37
|
let(:config) { double(:config, vm: vm_config) }
|
@@ -37,21 +41,42 @@ describe VagrantPlugins::Skytap::Action::PrepareNFSSettings do
|
|
37
41
|
let(:guest_ip) { '192.168.0.1' }
|
38
42
|
let(:using_nfs) { false }
|
39
43
|
|
44
|
+
let(:udpsocket) { double("UDPSocket") }
|
45
|
+
let(:addresses) { ["AF_INET", 99999, '127.0.0.1', host_ip] }
|
46
|
+
let(:socket) { double(:socket, connect: nil, addr: addresses) }
|
47
|
+
|
48
|
+
let(:nat_host_ip) { '10.0.4.1' }
|
49
|
+
let(:nat_guest_ip) { '14.0.0.1' }
|
50
|
+
let(:guest_network) { double(:guest_network, id: "2", attrs: {}, :nat_enabled? => false) }
|
51
|
+
let(:guest_iface) { double(:guest_iface, network: guest_network) }
|
52
|
+
let(:guest_vm) { double(:guest_vm, interfaces: [guest_iface]) }
|
53
|
+
|
54
|
+
let(:default_hostpath) {"."}
|
55
|
+
let(:default_disabled) { false }
|
56
|
+
let(:default_type) { nil }
|
40
57
|
let(:default_folder_props) do
|
41
|
-
{guestpath: "/vagrant", hostpath:
|
58
|
+
{guestpath: "/vagrant", hostpath: default_hostpath, disabled: default_disabled, type: default_type}
|
42
59
|
end
|
43
60
|
let(:synced_folders) { { "/vagrant" => default_folder_props } }
|
44
61
|
|
45
|
-
let(:instance)
|
62
|
+
let(:instance) do
|
63
|
+
described_class.new(app, env).tap do |instance|
|
64
|
+
allow(instance).to receive(:machine).and_return(machine)
|
65
|
+
allow(instance).to receive(:host_vm).and_return(host_vm)
|
66
|
+
allow(instance).to receive(:current_vm).and_return(guest_vm)
|
67
|
+
end
|
68
|
+
end
|
46
69
|
|
47
70
|
before do
|
48
|
-
|
49
|
-
allow(
|
50
|
-
allow(subject).to receive(:read_host_ip).and_return(host_ip)
|
71
|
+
stub_const("UDPSocket", udpsocket)
|
72
|
+
allow(udpsocket).to receive(:open).and_yield(socket)
|
51
73
|
end
|
52
74
|
|
53
75
|
describe "#call" do
|
54
76
|
subject {instance}
|
77
|
+
before do
|
78
|
+
allow(subject).to receive(:using_nfs?).and_return(using_nfs)
|
79
|
+
end
|
55
80
|
|
56
81
|
context "when not using nfs" do
|
57
82
|
it "does not set the host and guest ip addresses" do
|
@@ -82,25 +107,47 @@ describe VagrantPlugins::Skytap::Action::PrepareNFSSettings do
|
|
82
107
|
end
|
83
108
|
|
84
109
|
context "when its type is rsync" do
|
85
|
-
let(:
|
86
|
-
{guestpath: "/vagrant", hostpath: ".", disabled: false, type: :rsync}
|
87
|
-
end
|
110
|
+
let(:default_type) { :rsync }
|
88
111
|
it {should be false }
|
89
112
|
end
|
90
113
|
|
91
114
|
context "when its type is nfs" do
|
92
|
-
let(:
|
93
|
-
{guestpath: "/vagrant", hostpath: ".", disabled: false, type: :nfs}
|
94
|
-
end
|
115
|
+
let(:default_type) { :nfs }
|
95
116
|
it {should be true }
|
96
117
|
|
97
118
|
context "when it is disabled" do
|
98
|
-
let(:
|
99
|
-
{guestpath: "/vagrant", hostpath: ".", disabled: true, type: :nfs}
|
100
|
-
end
|
119
|
+
let(:default_disabled) { true }
|
101
120
|
it {should be false }
|
102
121
|
end
|
103
122
|
end
|
104
123
|
end
|
105
124
|
end
|
125
|
+
|
126
|
+
describe "read_host_ip" do
|
127
|
+
subject { instance.read_host_ip }
|
128
|
+
|
129
|
+
context "when not running in a VM" do
|
130
|
+
it { should eq host_ip }
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when running in a VM" do
|
134
|
+
let(:host_iface) { double(:host_iface, nat_address_for_network: nat_host_ip) }
|
135
|
+
let(:host_vm) { double(:host_vm, interfaces: [host_iface]) }
|
136
|
+
|
137
|
+
before do
|
138
|
+
allow(host_vm).to receive(:reload).and_return(host_vm)
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when guest network is not NAT enabled" do
|
142
|
+
it { should eq host_ip }
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when guest network is NAT enabled" do
|
146
|
+
before do
|
147
|
+
allow(guest_network).to receive(:nat_enabled?).and_return(true)
|
148
|
+
end
|
149
|
+
it { should eq nat_host_ip }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
106
153
|
end
|