vagrant-skytap 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 24c83e15798abd9dd2e7df78bd984c2768dae164
4
- data.tar.gz: 0198f2146818c63d8ef7fc0bf9808a20fdea288e
3
+ metadata.gz: df35f6df87630a3718560fe3900aa38b7eb7d946
4
+ data.tar.gz: 98caf819eed011f11be89c6072c5df6e3a8a4664
5
5
  SHA512:
6
- metadata.gz: 533b0663bcd1ceee2c749db4cc4bc95a77bb2a1a940d90422fdb118ae182b72dcf10dfc84ccde3510590193511eec4d24ae9421dbbeb4cd2c601db6a5d402804
7
- data.tar.gz: 231290cdc514f59aa84187f951a47b13742447a7a5fcfa78af9c1675c1e1efd02a4a2522c5e1f74817b39090ad10cc408b2c8c8fca031766b7ec6bbf30c8abb1
6
+ metadata.gz: d3798da927dafe1c1f69b8b2f5bbe5fb8ef8480c71fce6866f7ce268c476b7be4ce9ed0226e683e493f957d492d799c67ea9798e273f208fdce1ba32ca61c71c
7
+ data.tar.gz: 0641307fac52c06148d0554eae1455783834fe745a7bf7432086e27d9001a55660d3e3f41392df8561e69d52be4c78248954cf0aa5b981e6078f75666243747a
data/.gitignore CHANGED
@@ -19,11 +19,10 @@ Vagrantfile
19
19
  # RVM files for gemset/ruby setting
20
20
  .ruby-*
21
21
  .rvmrc
22
- .ruby-version
23
22
 
24
- # other source control
25
23
  .hg
26
24
  .hgignore
27
- .byebug_history
25
+ .agignore
26
+ .project
27
+ .ruby-version
28
28
  .project
29
- .rspec
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 0.2.6 (February 16, 2016)
2
+
3
+ * Changes to support logging in to base boxes with the default `vagrant` user and
4
+ insecure keypair. Previously, if the SSH username and password were omitted
5
+ from the `Vagrantfile`, the user was prompted to enter them. We now default
6
+ to the `vagrant` login. If the source VM (image) has saved credentials
7
+ (a Skytap-specific feature; see [Accessing and Saving VM Credentials](http://help.skytap.com/#VM_Settings_Credentials.html))
8
+ then the user will be shown a menu of the stored credentials,
9
+ as well as an option for the default `vagrant` login.
10
+
1
11
  # 0.2.5 (February 3, 2016)
2
12
 
3
13
  * Initial push to GitHub. Random cleanup, including getting rid of some unused test files.
data/README.md CHANGED
@@ -120,8 +120,6 @@ will install the provider only. It will be necess
120
120
  ### Sharing Environments via Published URLs
121
121
  The Skytap Vagrant provider has basic support for [published URLs](http://help.skytap.com/#Published_URLs.html). Publishing an environment gives full anonymous access to the Skytap environment to anyone with the URL (and optional password). This feature differs from [Vagrant Share](https://www.vagrantup.com/docs/share/index.html) in that the user will have browser-based access to a shared view of all VMs in the environment, including details at a glance, thumbnails, and desktop access using SmartClient.
122
122
 
123
- ![Shared environment view](//help.skytap.com/Content/Resources/Images/PublishedURLView.png)
124
-
125
123
 
126
124
  A password may be specified. Anonymous access may be revoked by deleting the published URL, using the `vagrant publish-url delete` subcommand. (Skytap users with appropriate
127
125
  permissions may still access the environment through the UI if desired.)
@@ -44,7 +44,9 @@ module VagrantPlugins
44
44
  end
45
45
 
46
46
  def using_nfs?
47
- @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }
47
+ @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }.tap do |ret|
48
+ @logger.debug("PrepareNFSSettings#using_nfs? returning #{ret}. Synced folders: #{@machine.config.vm.synced_folders.inspect}")
49
+ end
48
50
  end
49
51
 
50
52
  # Returns the IP address of the host, preferring one on an interface which
@@ -54,7 +56,7 @@ module VagrantPlugins
54
56
  s.connect(machine.ssh_info[:host], 1)
55
57
  s.addr.last
56
58
  end.tap do |ret|
57
- @logger.debug "read_host_ip returning #{ret}"
59
+ @logger.debug("PrepareNFSSettings#read_host_ip returning #{ret}")
58
60
  end
59
61
  end
60
62
 
@@ -63,6 +63,7 @@ module VagrantPlugins
63
63
  # This action is called to terminate the remote machine.
64
64
  def self.action_destroy
65
65
  Vagrant::Action::Builder.new.tap do |b|
66
+ b.use ConfigValidate
66
67
  b.use action_fetch_environment
67
68
  b.use Call, ExistenceCheck do |env, b1|
68
69
  case existence_state = env[:result]
@@ -98,6 +99,7 @@ module VagrantPlugins
98
99
  # This action is called when `vagrant provision` is called.
99
100
  def self.action_provision
100
101
  Vagrant::Action::Builder.new.tap do |b|
102
+ b.use ConfigValidate
101
103
  b.use action_fetch_environment
102
104
  b.use Call, ExistenceCheck do |env, b1|
103
105
  case result = env[:result]
@@ -228,6 +230,7 @@ module VagrantPlugins
228
230
  def self.action_create
229
231
  Vagrant::Action::Builder.new.tap do |b|
230
232
  b.use HandleBox
233
+ b.use ConfigValidate
231
234
  b.use action_fetch_environment
232
235
  b.use ComposeEnvironment
233
236
  end
@@ -281,6 +284,7 @@ module VagrantPlugins
281
284
 
282
285
  def self.action_reload
283
286
  Vagrant::Action::Builder.new.tap do |b|
287
+ b.use ConfigValidate
284
288
  b.use action_fetch_environment
285
289
  b.use Call, ExistenceCheck do |env, b1|
286
290
  case env[:result]
@@ -300,7 +304,6 @@ module VagrantPlugins
300
304
 
301
305
  def self.action_fetch_environment
302
306
  Vagrant::Action::Builder.new.tap do |b|
303
- b.use ConfigValidate
304
307
  b.use InitializeAPIClient
305
308
  b.use FetchEnvironment
306
309
  end
@@ -66,74 +66,54 @@ module VagrantPlugins
66
66
 
67
67
  def ask_credentials
68
68
  @logger.debug("ask_credentials")
69
- return if username && password
70
69
 
71
- env[:ui].info("Note that the machine password will be stored in " \
72
- "cleartext on your local filesystem.")
73
-
74
- creds = current_vm.credentials.select(&:recognized?)
75
-
76
- if username
77
- env[:ui].info("SSH username found in Vagrantfile: #{username}")
78
- match = creds.detect{|c| c.username == username}
79
-
80
- if match
81
- @logger.info("Found username in Vagrantfile. Using matching password from credentials.")
82
- env[:ui].info("Matched SSH password in Skytap VM credentials.")
83
- @password = match.password
84
- else
85
- @logger.info("Found username in Vagrantfile. Will use manual password entry.")
86
- end
87
- elsif creds.present?
88
- question = "How do you want to choose SSH credentials for machine '#{@machine.name}'?"
89
- choices = creds.collect do |c|
90
- "Use VM credentials stored in Skytap: #{c}"
91
- end
92
- choices << 'Type credentials manually'
93
-
94
- ask_from_list(question, choices, 0) do |i|
95
- if cred = creds[i]
96
- @username = cred.username
97
- @password = cred.password
70
+ if !username
71
+ creds = current_vm.credentials.select(&:recognized?)
72
+ if creds.present?
73
+ question = "How do you want to choose SSH credentials for machine '#{@machine.name}'?\n" \
74
+ "Note that credentials retrieved from the Skytap VM will be stored in cleartext on your local filesystem."
75
+ ask_from_list(question, credentials_choices(creds), 0) do |i|
76
+ if cred = creds[i]
77
+ @username = cred.username
78
+ @password = cred.password
79
+ end
98
80
  end
81
+ else
82
+ @logger.info("No login credentials found for the VM. Allowing fallback to default vagrant login.")
99
83
  end
100
- else
101
- @logger.info("No login credentials found for the VM. Prompting for manual username/password entry.")
102
84
  end
103
-
104
- @username ||= ask_username
105
- @password ||= ask_password
85
+ [@username, @password]
106
86
  end
107
87
 
108
- def ask_username
109
- @username = ask('Enter SSH username:').strip
110
- end
111
-
112
- def ask_password
113
- @password = ask('Enter SSH password (no output will appear):', echo: false).strip
88
+ def credentials_choices(creds)
89
+ creds.collect do |c|
90
+ "Use VM credentials stored in Skytap: #{c}"
91
+ end.tap do |choices|
92
+ choices << "Don't specify credentials: Use standard 'vagrant' login with insecure keypair"
93
+ end
114
94
  end
115
95
 
116
96
  def ask_routing
117
97
  @logger.debug("ask_routing")
118
- return if host && port
119
-
120
- iface = current_vm.interfaces.first
121
- choices = connection_choices(iface).select(&:valid?)
122
- raise Errors::NoConnectionOptions unless choices.present?
123
-
124
- if vpn_url = @provider_config.vpn_url
125
- choice = choices.detect do |choice|
126
- choice.vpn && vpn_url.include?(choice.vpn.id)
98
+ unless host && port
99
+ iface = current_vm.interfaces.first
100
+ choices = connection_choices(iface).select(&:valid?)
101
+ raise Errors::NoConnectionOptions unless choices.present?
102
+
103
+ if vpn_url = @provider_config.vpn_url
104
+ choice = choices.detect do |choice|
105
+ choice.vpn && vpn_url.include?(choice.vpn.id)
106
+ end
107
+ raise Errors::DoesNotExist, object_name: vpn_url unless choice
108
+ @host, @port = choice.choose
109
+ else
110
+ 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
113
+ end
127
114
  end
128
- raise Errors::DoesNotExist, object_name: vpn_url unless choice
129
- @host, @port = choice.choose
130
- return
131
- end
132
-
133
- question = "How do you want to connect to machine '#{@machine.name}'?"
134
- ask_from_list(question, choices, 0) do |i, choice|
135
- @host, @port = choice.choose
136
115
  end
116
+ [@host, @port]
137
117
  end
138
118
 
139
119
  def connection_choices(iface)
@@ -22,6 +22,6 @@
22
22
 
23
23
  module VagrantPlugins
24
24
  module Skytap
25
- VERSION = "0.2.5"
25
+ VERSION = "0.2.6"
26
26
  end
27
27
  end
@@ -0,0 +1,53 @@
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_relative 'base'
24
+
25
+ describe VagrantPlugins::Skytap::API::Credentials do
26
+ include_context "rest_api"
27
+
28
+ let(:instance) { described_class.new(credential_attrs, nil, {}) }
29
+
30
+ describe "recognized?" do
31
+ subject { instance.recognized? }
32
+
33
+ context "when text is xxx/yyy" do
34
+ let(:credential_attrs) {{ "text" => "xxx/yyy" }}
35
+ it { should be true }
36
+ end
37
+
38
+ context "when text is xxx / yyy" do
39
+ let(:credential_attrs) {{ "text" => "xxx / yyy" }}
40
+ it { should be true }
41
+ end
42
+
43
+ context "when text is blank" do
44
+ let(:credential_attrs) {{ "text" => "" }}
45
+ it { should be false }
46
+ end
47
+
48
+ context "when text is xxx, yyy" do
49
+ let(:credential_attrs) {{ "text" => "xxx, yyy" }}
50
+ it { should be false }
51
+ end
52
+ end
53
+ end
@@ -22,60 +22,56 @@
22
22
 
23
23
  require_relative 'base'
24
24
  require "vagrant-skytap/setup_helper"
25
- require "vagrant-skytap/api/environment"
26
- require "vagrant-skytap/api/vpn"
27
- require "vagrant-skytap/api/vpn_attachment"
28
- require "vagrant-skytap/config"
29
25
 
30
26
  describe VagrantPlugins::Skytap::SetupHelper do
31
27
  include_context "rest_api"
32
28
 
33
- let(:instance) { described_class.new }
34
- let(:json_path) { File.join(File.expand_path('..', __FILE__), 'support', 'api_responses') }
29
+ let(:json_path) { File.join(File.expand_path('..', __FILE__), 'support', 'api_responses') }
30
+ let(:vpn_attachment_attrs) { read_json(json_path, 'vpn_attachment1.json') }
35
31
 
36
- let(:vm1_attrs) { read_json(json_path, 'vm1.json') }
37
- let(:network1_attrs) { read_json(json_path, 'network1.json') }
38
- let(:vpn1_attrs) { read_json(json_path, 'vpn1.json') }
39
- let(:vpn_attachment1_attrs) { read_json(json_path, 'vpn_attachment1.json') }
40
- let(:empty_environment_attrs) { read_json(json_path, 'empty_environment.json')}
41
-
42
- let(:network_attrs) do
43
- network1_attrs.dup.tap do |ret|
44
- ret['vpn_attachments'] = [vpn_attachment1_attrs]
32
+ let(:vm_attrs) do
33
+ read_json(json_path, 'vm1.json').tap do |ret|
34
+ ret['interfaces'].first['nat_addresses']['vpn_nat_addresses'] = {}
45
35
  end
46
36
  end
47
37
 
48
- let(:vm_attrs) do
49
- vm1_attrs.dup.tap do |ret|
50
- ret['interfaces'].first['nat_addresses']['vpn_nat_addresses'] = {}
38
+ let(:network_attrs) do
39
+ read_json(json_path, 'network1.json').tap do |ret|
40
+ ret['vpn_attachments'] = [vpn_attachment_attrs]
51
41
  end
52
42
  end
53
43
 
54
44
  let(:environment_attrs) do
55
- empty_environment_attrs.dup.tap do |ret|
56
- ret['vms'] = [vm_attrs]
57
- ret['networks'] = [network1_attrs]
45
+ read_json(json_path, 'empty_environment.json').tap do |ret|
46
+ ret['vms'] = [vm_attrs]
47
+ ret['networks'] = [network_attrs]
58
48
  end
59
49
  end
60
50
 
61
- let(:environment) { VagrantPlugins::Skytap::API::Environment.new(environment_attrs, env) }
51
+ let(:environment) do
52
+ API::Environment.new(environment_attrs, env)
53
+ end
62
54
 
63
55
  let(:vpn_attrs) do
64
- vpn1_attrs.dup.tap do |ret|
65
- ret['network_attachments'] = [vpn_attachment1_attrs]
56
+ read_json(json_path, 'vpn1.json').dup.tap do |ret|
57
+ ret['network_attachments'] = [vpn_attachment_attrs]
66
58
  end
67
59
  end
68
60
 
69
- let(:vpn) {VagrantPlugins::Skytap::API::Vpn.new(vpn_attrs, env)}
61
+ let(:vpn) {API::Vpn.new(vpn_attrs, env)}
62
+ let(:vpns) {[vpn]}
63
+ let(:vpn_choice) {double(:vpn_choice, vpn: vpn, choose: ["1.2.3.4", 22], :valid? => true)}
64
+ let(:user_input) { "" }
70
65
 
71
66
  let(:ssh_config) do
72
- double(:ssh, username: "foo", password: "bar", host: nil, port: nil)
67
+ double(:ssh_config, username: nil, password: nil, host: nil, port: nil)
73
68
  end
74
69
  let(:machine_config) do
75
70
  double(:machine_config, ssh: ssh_config)
76
71
  end
72
+ let(:vpn_url) {"/vpns/vpn-123"}
77
73
  let(:provider_config) do
78
- double(:provider_config, vm_url: "/vms/1", username: "jsmith", api_token: "123123", base_url: base_url)
74
+ double(:provider_config, vm_url: "/vms/1", username: "jsmith", api_token: "123123", base_url: base_url, vpn_url: vpn_url)
79
75
  end
80
76
  let(:api_client) { API::Client.new(provider_config) }
81
77
  let(:machine) { double(:machine, name: "vm1", id: "6981850", config: machine_config, provider_config: provider_config) }
@@ -85,48 +81,115 @@ describe VagrantPlugins::Skytap::SetupHelper do
85
81
  before :each do
86
82
  # Ensure tests are not affected by Skytap credential environment variables
87
83
  ENV.stub(:[] => nil)
88
- stub_request(:get, /.*/).to_return(body: '{}', status: 200)
89
- stub_request(:get, %r{/configurations/\d+}).to_return(body: JSON.dump(environment_attrs), status: 200)
90
- stub_request(:get, %r{/vpns$}).to_return(body: JSON.dump([vpn_attrs]), status: 200)
84
+ allow(ui).to receive(:ask).and_return(user_input)
85
+ allow(instance).to receive(:vpns).and_return(vpns)
86
+ allow(vpn).to receive(:choice_for_setup).and_return(vpn_choice)
87
+ stub_request(:get, /.*/).to_return(body: "{}", status: 200)
88
+ end
89
+
90
+ describe "connection_choices" do
91
+ subject do
92
+ instance.send(:connection_choices, instance.current_vm.interfaces.first)
93
+ end
94
+
95
+ context "when there are choices" do
96
+ it "should return a matching choice" do
97
+ expect(subject.count).to be 1
98
+ expect(subject.first.vpn).to be vpn
99
+ end
100
+ end
101
+
102
+ context "when there are no choices" do
103
+ let(:vpns) {[]}
104
+ it { should eq [] }
105
+ end
91
106
  end
92
107
 
93
108
  describe "ask_routing" do
94
109
  subject do
95
- instance
110
+ instance.send(:ask_routing)
96
111
  end
97
112
 
98
- before :each do
99
- allow(subject).to receive(:vpns).and_return([vpn])
113
+ context "when valid vpn_url specified" do
114
+ it {should eq ["1.2.3.4", 22]}
100
115
  end
101
116
 
102
- after(:each) do
103
- allow_any_instance_of(VpnChoice).to receive(:choose).and_call_original
117
+ context "when invalid vpn_url specified" do
118
+ let(:vpn_url) {"bogus"}
119
+ it "raises error" do
120
+ expect{subject}.to raise_error Errors::DoesNotExist
121
+ end
104
122
  end
105
123
 
106
- it "has connection_choices" do
107
- interface = subject.current_vm.interfaces.first
108
- choices = subject.send(:connection_choices, interface)
109
- expect(choices.count).to eq 1
110
- expect(choices.first.vpn).to_not be_nil
124
+ context "when vpn_url unspecified" do
125
+ let(:vpn_url) {nil}
126
+ let(:user_input) {"1"}
127
+ it {should eq ["1.2.3.4", 22]}
111
128
  end
112
129
 
113
- it "does not show choices if vpn_url specified" do
114
- allow(provider_config).to receive(:vpn_url).and_return(vpn.url)
115
- vpn_choice = double(:choice, vpn: vpn, choose: ['1.2.3.4', 22], :valid? => true)
116
- allow(vpn).to receive(:choice_for_setup).and_return(vpn_choice)
117
- expect(subject).not_to receive(:ask_from_list)
118
- subject.send(:ask_routing)
130
+ context "when no valid vpns exist" do
131
+ before do
132
+ allow(vpn_choice).to receive(:valid?).and_return(false)
133
+ end
134
+ it "raises error" do
135
+ expect{subject}.to raise_error Errors::NoConnectionOptions
136
+ end
119
137
  end
138
+ end
120
139
 
121
- it "raises DoesNotExist if non-existent vpn_url specified" do
122
- allow(provider_config).to receive(:vpn_url).and_return("bogus")
123
- expect{subject.send(:ask_routing)}.to raise_error(Errors::DoesNotExist)
140
+ describe "ask_credentials" do
141
+ subject do
142
+ instance.send(:ask_credentials)
124
143
  end
125
144
 
126
- it "shows choices if vpn_url unspecified" do
127
- allow(provider_config).to receive(:vpn_url).and_return(nil)
128
- expect(subject).to receive(:ask_from_list)
129
- subject.send(:ask_routing)
145
+ context "when username and password are set" do
146
+ let(:ssh_config) {double(:ssh_config, username: "foo", password: "bar", host: nil, port: nil)}
147
+ it {should eq %w[foo bar]}
148
+ end
149
+
150
+ context "when only username is set and it matches a set of stored credentials" do
151
+ let(:ssh_config) {double(:ssh_config, username: "skytap", password: nil, host: nil, port: nil)}
152
+ it {should eq ["skytap", nil]}
153
+ end
154
+
155
+ context "when username and password are not set but stored credentials exist" do
156
+ let(:user_input) {"1"}
157
+ it {should eq %w[skytap skypass]}
158
+ end
159
+
160
+ context "when username and password are not set and no stored credentials exist" do
161
+ before do
162
+ allow(instance.current_vm).to receive(:credentials).and_return([])
163
+ end
164
+ it {should eq [nil, nil]}
165
+ end
166
+ end
167
+
168
+ describe "ask_from_list" do
169
+ subject do
170
+ instance.send(:ask_from_list, "Prompt", ["aaa", "bbb"], 0)
171
+ end
172
+
173
+ context "when user enters nothing" do
174
+ let(:user_input) {""}
175
+ it {should eq 0}
176
+ end
177
+
178
+ context "when user enters numeral" do
179
+ let(:user_input) {" 2 "}
180
+ it {should eq 1}
181
+ end
182
+ end
183
+
184
+ describe "credentials_choices" do
185
+ subject do
186
+ instance.send(:credentials_choices, instance.current_vm.credentials)
187
+ end
188
+ it "returns a list of the vm's credentials plus the default vagrant login" do
189
+ expect(subject).to eq([
190
+ "Use VM credentials stored in Skytap: skytap / skypass",
191
+ "Don't specify credentials: Use standard 'vagrant' login with insecure keypair"
192
+ ])
130
193
  end
131
194
  end
132
195
  end
@@ -72,7 +72,7 @@
72
72
  "credentials": [
73
73
  {
74
74
  "id": "5695620",
75
- "text": "xxx / yyy"
75
+ "text": "skytap / skypass"
76
76
  }
77
77
  ],
78
78
  "desktop_resizable": true,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-skytap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric True
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-04 00:00:00.000000000 Z
12
+ date: 2016-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json_pure
@@ -216,6 +216,7 @@ files:
216
216
  - spec/unit/actions/update_hardware_spec.rb
217
217
  - spec/unit/base.rb
218
218
  - spec/unit/config_spec.rb
219
+ - spec/unit/credentials_spec.rb
219
220
  - spec/unit/environment_spec.rb
220
221
  - spec/unit/forwarded_port_spec.rb
221
222
  - spec/unit/hosts/common/cap/ssh_tunnel_spec.rb