vagrant-skytap 0.2.10 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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