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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/lib/vagrant-skytap/action/check_created.rb +43 -0
  4. data/lib/vagrant-skytap/action/check_running.rb +43 -0
  5. data/lib/vagrant-skytap/action/get_host_vm.rb +52 -0
  6. data/lib/vagrant-skytap/action/prepare_nfs_settings.rb +22 -5
  7. data/lib/vagrant-skytap/action.rb +11 -18
  8. data/lib/vagrant-skytap/api/connectable.rb +50 -0
  9. data/lib/vagrant-skytap/api/environment.rb +10 -5
  10. data/lib/vagrant-skytap/api/interface.rb +9 -0
  11. data/lib/vagrant-skytap/api/network.rb +60 -1
  12. data/lib/vagrant-skytap/api/public_ip.rb +7 -75
  13. data/lib/vagrant-skytap/api/publish_set.rb +4 -0
  14. data/lib/vagrant-skytap/api/published_service.rb +7 -71
  15. data/lib/vagrant-skytap/api/resource.rb +25 -3
  16. data/lib/vagrant-skytap/api/tunnel.rb +69 -0
  17. data/lib/vagrant-skytap/api/vm.rb +10 -2
  18. data/lib/vagrant-skytap/api/vpn.rb +2 -106
  19. data/lib/vagrant-skytap/cap/host_metadata.rb +45 -0
  20. data/lib/vagrant-skytap/connection/public_ip_choice.rb +94 -0
  21. data/lib/vagrant-skytap/connection/published_service_choice.rb +100 -0
  22. data/lib/vagrant-skytap/connection/tunnel_choice.rb +118 -0
  23. data/lib/vagrant-skytap/connection/vpn_choice.rb +132 -0
  24. data/lib/vagrant-skytap/connection.rb +123 -0
  25. data/lib/vagrant-skytap/errors.rb +4 -0
  26. data/lib/vagrant-skytap/plugin.rb +5 -0
  27. data/lib/vagrant-skytap/setup_helper.rb +34 -8
  28. data/lib/vagrant-skytap/version.rb +1 -1
  29. data/locales/en.yml +46 -0
  30. data/spec/unit/actions/prepare_nfs_settings_spec.rb +63 -16
  31. data/spec/unit/cap/host_metadata_spec.rb +43 -0
  32. data/spec/unit/connections/public_ip_choice_spec.rb +57 -0
  33. data/spec/unit/connections/published_service_choice_spec.rb +79 -0
  34. data/spec/unit/connections/tunnel_choice_spec.rb +124 -0
  35. data/spec/unit/connections/vpn_choice_spec.rb +109 -0
  36. data/spec/unit/interface_spec.rb +53 -0
  37. data/spec/unit/network_spec.rb +123 -0
  38. data/spec/unit/setup_helper_spec.rb +59 -19
  39. data/spec/unit/support/api_responses/tunnel1.json +7 -0
  40. data/spec/unit/support/api_responses/vm1.json +12 -1
  41. data/spec/unit/support/shared/rest_api_context.rb +1 -0
  42. data/spec/unit/tunnel_spec.rb +62 -0
  43. data/spec/unit/vm_spec.rb +53 -60
  44. 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).select(&:valid?)
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
- if vpn_url = @provider_config.vpn_url
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
- @host, @port = choice.choose
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, choice|
112
- @host, @port = choice.choose
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
- vpn_choices(iface)
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
@@ -22,6 +22,6 @@
22
22
 
23
23
  module VagrantPlugins
24
24
  module Skytap
25
- VERSION = "0.2.10"
25
+ VERSION = "0.3.0"
26
26
  end
27
27
  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(:app) { lambda { |env| } }
30
- let(:env) { { machine: machine} }
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: ".", disabled: false}
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) { described_class.new(app, env) }
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
- allow(instance).to receive(:machine).and_return(machine)
49
- allow(subject).to receive(:using_nfs?).and_return(using_nfs)
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(:default_folder_props) do
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(:default_folder_props) do
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(:default_folder_props) do
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