ridley 0.10.0.rc1 → 0.10.0.rc2
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.
- data/bootstrappers/windows_omnibus.erb +1 -22
- data/lib/ridley/bootstrap_bindings/windows_template_binding.rb +0 -53
- data/lib/ridley/bootstrapper.rb +13 -10
- data/lib/ridley/bootstrapper/context.rb +2 -1
- data/lib/ridley/errors.rb +3 -0
- data/lib/ridley/host_connector.rb +21 -16
- data/lib/ridley/host_connector/ssh/worker.rb +40 -2
- data/lib/ridley/host_connector/winrm.rb +1 -0
- data/lib/ridley/host_connector/winrm/command_uploader.rb +90 -0
- data/lib/ridley/host_connector/winrm/worker.rb +67 -47
- data/lib/ridley/resources/environment_resource.rb +1 -1
- data/lib/ridley/resources/node_resource.rb +106 -19
- data/lib/ridley/version.rb +1 -1
- data/spec/unit/ridley/bootstrap_bindings/windows_template_binding_spec.rb +0 -6
- data/spec/unit/ridley/bootstrapper_spec.rb +23 -3
- data/spec/unit/ridley/host_connector/ssh/worker_spec.rb +49 -7
- data/spec/unit/ridley/host_connector/ssh_spec.rb +4 -3
- data/spec/unit/ridley/host_connector/winrm/command_uploader_spec.rb +67 -0
- data/spec/unit/ridley/host_connector/winrm/worker_spec.rb +67 -21
- data/spec/unit/ridley/host_connector/winrm_spec.rb +5 -0
- data/spec/unit/ridley/host_connector_spec.rb +39 -10
- data/spec/unit/ridley/resources/node_resource_spec.rb +171 -4
- metadata +5 -2
@@ -40,7 +40,7 @@ module Ridley
|
|
40
40
|
# @example setting and saving an environment level default attribute
|
41
41
|
#
|
42
42
|
# obj = environment.find("production")
|
43
|
-
# obj.
|
43
|
+
# obj.set_default_attribute("my_app.billing.enabled", false)
|
44
44
|
# obj.save
|
45
45
|
#
|
46
46
|
# @param [String] key
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Ridley
|
1
|
+
module Ridley
|
2
2
|
# @author Jamie Winsor <reset@riotgames.com>
|
3
3
|
class NodeResource < Ridley::Resource
|
4
4
|
class << self
|
@@ -54,6 +54,65 @@ module Ridley
|
|
54
54
|
Bootstrapper.new(args, options).run
|
55
55
|
end
|
56
56
|
|
57
|
+
# Executes a Chef run using the best worker available for the given
|
58
|
+
# host.
|
59
|
+
#
|
60
|
+
# @param [Ridley::Client] client
|
61
|
+
# @param [String] host
|
62
|
+
#
|
63
|
+
# @return [HostConnector::Response]
|
64
|
+
def chef_run(client, host)
|
65
|
+
worker = configured_worker_for(client, host)
|
66
|
+
worker.chef_client
|
67
|
+
ensure
|
68
|
+
worker.terminate if worker && worker.alive?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Puts a secret on the host using the best worker available for
|
72
|
+
# the given host.
|
73
|
+
#
|
74
|
+
# @param [Ridley::Client] client
|
75
|
+
# @param [String] host
|
76
|
+
# @param [String] encrypted_data_bag_secret_path
|
77
|
+
#
|
78
|
+
# @return [HostConnector::Response]
|
79
|
+
def put_secret(client, host, encrypted_data_bag_secret_path)
|
80
|
+
worker = configured_worker_for(client, host)
|
81
|
+
worker.put_secret(encrypted_data_bag_secret_path)
|
82
|
+
ensure
|
83
|
+
worker.terminate if worker && worker.alive?
|
84
|
+
end
|
85
|
+
|
86
|
+
# Executes an arbitrary ruby script using the best worker available
|
87
|
+
# for the given host.
|
88
|
+
#
|
89
|
+
# @param [Ridley::Client] client
|
90
|
+
# @param [String] host
|
91
|
+
# @param [Array<String>] command_lines
|
92
|
+
#
|
93
|
+
# @return [HostConnector::Response]
|
94
|
+
def ruby_script(client, host, command_lines)
|
95
|
+
worker = configured_worker_for(client, host)
|
96
|
+
worker.ruby_script(command_lines)
|
97
|
+
ensure
|
98
|
+
worker.terminate if worker && worker.alive?
|
99
|
+
end
|
100
|
+
|
101
|
+
# Executes the given command on a node using the best worker
|
102
|
+
# available for the given host.
|
103
|
+
#
|
104
|
+
# @param [Ridley::Client] client
|
105
|
+
# @param [String] host
|
106
|
+
# @param [String] command
|
107
|
+
#
|
108
|
+
# @return [Array<Symbol, HostConnector::Response>]
|
109
|
+
def execute_command(client, host, command)
|
110
|
+
worker = configured_worker_for(client, host)
|
111
|
+
worker.run(command)
|
112
|
+
ensure
|
113
|
+
worker.terminate if worker && worker.alive?
|
114
|
+
end
|
115
|
+
|
57
116
|
# Merges the given data with the the data of the target node on the remote
|
58
117
|
#
|
59
118
|
# @param [Ridley::Client] client
|
@@ -72,8 +131,25 @@ module Ridley
|
|
72
131
|
def merge_data(client, target, options = {})
|
73
132
|
find!(client, target).merge_data(options)
|
74
133
|
end
|
134
|
+
|
135
|
+
private
|
136
|
+
# @param [Ridley::Client] client
|
137
|
+
# @param [String] host
|
138
|
+
#
|
139
|
+
# @return [SSH::Worker, WinRM::Worker]
|
140
|
+
def configured_worker_for(client, host)
|
141
|
+
connector_options = Hash.new
|
142
|
+
connector_options[:ssh] = client.ssh
|
143
|
+
connector_options[:winrm] = client.winrm
|
144
|
+
|
145
|
+
HostConnector.best_connector_for(host, connector_options) do |host_connector|
|
146
|
+
host_connector::Worker.new(host, connector_options)
|
147
|
+
end
|
148
|
+
end
|
75
149
|
end
|
76
150
|
|
151
|
+
include Ridley::Logging
|
152
|
+
|
77
153
|
set_chef_id "name"
|
78
154
|
set_chef_type "node"
|
79
155
|
set_chef_json_class "Chef::Node"
|
@@ -114,8 +190,8 @@ module Ridley
|
|
114
190
|
# attribute and value.
|
115
191
|
#
|
116
192
|
# @note It is not possible to set any other attribute level on a node and have it persist after
|
117
|
-
# a Chef Run. This is because all other attribute levels are truncated at the start of a Chef Run.
|
118
|
-
#
|
193
|
+
# a Chef Run. This is because all other attribute levels are truncated at the start of a Chef Run.
|
194
|
+
#
|
119
195
|
# @example setting and saving a node level normal attribute
|
120
196
|
#
|
121
197
|
# obj = node.find("jwinsor-1")
|
@@ -165,7 +241,7 @@ module Ridley
|
|
165
241
|
#
|
166
242
|
# @return [nil, String]
|
167
243
|
def cloud_provider
|
168
|
-
self.cloud? ? self.automatic[:cloud][:provider] : nil
|
244
|
+
self.cloud? ? self.automatic[:cloud][:provider] : nil
|
169
245
|
end
|
170
246
|
|
171
247
|
# Returns true if the node is identified as a cloud node.
|
@@ -196,18 +272,25 @@ module Ridley
|
|
196
272
|
self.cloud_provider == "rackspace"
|
197
273
|
end
|
198
274
|
|
199
|
-
# Run Chef-Client on the instantiated node
|
275
|
+
# Run Chef-Client on the instantiated node.
|
200
276
|
#
|
201
277
|
# @param [Hash] options
|
202
|
-
# a hash of options to pass to {Ridley::
|
278
|
+
# a hash of options to pass to the best {Ridley::HostConnector}
|
203
279
|
#
|
204
|
-
# @return [
|
280
|
+
# @return [HostConnector::Response]
|
205
281
|
def chef_client(options = {})
|
206
|
-
|
282
|
+
connector_options = Hash.new
|
283
|
+
connector_options[:ssh] = client.ssh
|
284
|
+
connector_options[:winrm] = client.winrm
|
285
|
+
connector_options.merge(options)
|
286
|
+
|
287
|
+
log.debug "Running Chef Client on: #{self.public_hostname}"
|
207
288
|
|
208
|
-
|
209
|
-
|
210
|
-
|
289
|
+
HostConnector.best_connector_for(self.public_hostname, connector_options) do |host_connector|
|
290
|
+
host_connector.start(self, connector_options) do |connector|
|
291
|
+
_, response = connector.chef_client
|
292
|
+
response
|
293
|
+
end
|
211
294
|
end
|
212
295
|
end
|
213
296
|
|
@@ -216,9 +299,9 @@ module Ridley
|
|
216
299
|
# returned
|
217
300
|
#
|
218
301
|
# @param [Hash] options
|
219
|
-
# a hash of options to pass to {Ridley::
|
302
|
+
# a hash of options to pass to the best {Ridley::HostConnector}
|
220
303
|
#
|
221
|
-
# @return [
|
304
|
+
# @return [HostConnector::Response, nil]
|
222
305
|
def put_secret(options = {})
|
223
306
|
if client.encrypted_data_bag_secret_path.nil? ||
|
224
307
|
!File.exists?(client.encrypted_data_bag_secret_path)
|
@@ -226,13 +309,17 @@ module Ridley
|
|
226
309
|
return nil
|
227
310
|
end
|
228
311
|
|
229
|
-
|
230
|
-
|
231
|
-
|
312
|
+
connector_options = Hash.new
|
313
|
+
connector_options[:ssh] = client.ssh
|
314
|
+
connector_options[:winrm] = client.winrm
|
315
|
+
|
316
|
+
log.debug "Writing Encrypted Data Bag Secret to: #{self.public_hostname}"
|
232
317
|
|
233
|
-
|
234
|
-
|
235
|
-
|
318
|
+
HostConnector.best_connector_for(self.public_hostname, connector_options) do |host_connector|
|
319
|
+
host_connector.start(self, connector_options) do |connector|
|
320
|
+
_, response = connector.put_secret(client.encrypted_data_bag_secret_path)
|
321
|
+
response
|
322
|
+
end
|
236
323
|
end
|
237
324
|
end
|
238
325
|
|
data/lib/ridley/version.rb
CHANGED
@@ -104,12 +104,6 @@ describe Ridley::WindowsTemplateBinding do
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
107
|
-
describe "#windows_wget_vb" do
|
108
|
-
it "returns a string" do
|
109
|
-
subject.windows_wget_vb.should be_a(String)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
107
|
describe "#windows_wget_powershell" do
|
114
108
|
it "returns a string" do
|
115
109
|
subject.windows_wget_powershell.should be_a(String)
|
@@ -64,9 +64,29 @@ describe Ridley::Bootstrapper do
|
|
64
64
|
end
|
65
65
|
|
66
66
|
describe "#contexts" do
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
before do
|
68
|
+
Ridley::Bootstrapper::Context.stub(:create).and_return(double)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "creates a new context for each host" do
|
72
|
+
Ridley::Bootstrapper::Context.should_receive(:create).exactly(nodes.length).times
|
73
|
+
subject.contexts
|
74
|
+
end
|
75
|
+
|
76
|
+
it "contains a item for each host" do
|
77
|
+
subject.contexts.should have(nodes.length).items
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when a host is unreachable" do
|
81
|
+
before do
|
82
|
+
Ridley::Bootstrapper::Context.stub(:create).and_raise(Ridley::Errors::HostConnectionError)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "raises a HostConnectionError" do
|
86
|
+
expect {
|
87
|
+
subject.contexts
|
88
|
+
}.to raise_error(Ridley::Errors::HostConnectionError)
|
89
|
+
end
|
70
90
|
end
|
71
91
|
end
|
72
92
|
|
@@ -1,15 +1,57 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Ridley::HostConnector::SSH::Worker do
|
4
|
-
|
5
|
-
|
4
|
+
subject { ssh_worker }
|
5
|
+
let(:ssh_worker) { described_class.new(host, options) }
|
6
|
+
|
7
|
+
let(:host) { 'reset.riotgames.com' }
|
8
|
+
let(:options) { {} }
|
9
|
+
|
10
|
+
describe "#sudo" do
|
11
|
+
subject { ssh_worker.sudo }
|
12
|
+
|
13
|
+
it { should be_false }
|
14
|
+
|
15
|
+
context "with sudo" do
|
16
|
+
let(:options) { { ssh: { sudo: true } } }
|
17
|
+
|
18
|
+
it { should be_true }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#chef_client" do
|
23
|
+
subject(:chef_client) { ssh_worker.chef_client }
|
24
|
+
|
25
|
+
it { should be_a(Array) }
|
26
|
+
|
27
|
+
context "with sudo" do
|
28
|
+
let(:options) { { ssh: { sudo: true } } }
|
29
|
+
|
30
|
+
it "sends a run command with sudo" do
|
31
|
+
ssh_worker.should_receive(:run).with("sudo chef-client")
|
32
|
+
chef_client
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#put_secret" do
|
38
|
+
subject(:put_secret) { ssh_worker.put_secret(encrypted_data_bag_secret_path) }
|
39
|
+
let(:encrypted_data_bag_secret_path) { fixtures_path.join("encrypted_data_bag_secret").to_s }
|
40
|
+
let(:secret) { File.read(encrypted_data_bag_secret_path).chomp }
|
6
41
|
|
7
|
-
|
42
|
+
it "receives a run command with echo" do
|
43
|
+
ssh_worker.should_receive(:run).with("echo '#{secret}' > /etc/chef/encrypted_data_bag_secret; chmod 0600 /etc/chef/encrypted_data_bag_secret")
|
44
|
+
put_secret
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#ruby_script" do
|
49
|
+
subject(:ruby_script) { ssh_worker.ruby_script(command_lines) }
|
50
|
+
let(:command_lines) { ["puts 'hello'", "puts 'there'"] }
|
8
51
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
it { subject.new(host).sudo.should be_false }
|
52
|
+
it "receives a ruby call with the command" do
|
53
|
+
ssh_worker.should_receive(:run).with("#{described_class::EMBEDDED_RUBY_PATH} -e \"puts 'hello';puts 'there'\"")
|
54
|
+
ruby_script
|
13
55
|
end
|
14
56
|
end
|
15
57
|
end
|
@@ -18,7 +18,8 @@ describe Ridley::HostConnector::SSH do
|
|
18
18
|
let(:options) do
|
19
19
|
{
|
20
20
|
user: "vagrant",
|
21
|
-
password: "vagrant"
|
21
|
+
password: "vagrant",
|
22
|
+
timeout: 1
|
22
23
|
}
|
23
24
|
end
|
24
25
|
|
@@ -38,10 +39,10 @@ describe Ridley::HostConnector::SSH do
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
subject { Ridley::HostConnector::SSH.new([node_one, node_two], user: "vagrant", password: "vagrant") }
|
42
|
+
subject { Ridley::HostConnector::SSH.new([node_one, node_two], ssh: {user: "vagrant", password: "vagrant", timeout: 1}) }
|
42
43
|
|
43
44
|
describe "#run" do
|
44
|
-
it "returns an
|
45
|
+
it "returns an HostConnector::ResponseSet" do
|
45
46
|
subject.run("ls").should be_a(Ridley::HostConnector::ResponseSet)
|
46
47
|
end
|
47
48
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ridley::HostConnector::WinRM::CommandUploader do
|
4
|
+
let(:winrm_stub) {
|
5
|
+
double('WinRM',
|
6
|
+
run_cmd: run_cmd_data,
|
7
|
+
powershell: nil
|
8
|
+
)
|
9
|
+
}
|
10
|
+
|
11
|
+
subject { command_uploader }
|
12
|
+
|
13
|
+
let(:command_uploader) { described_class.new(winrm_stub) }
|
14
|
+
let(:command_string) { "a" * 2048 }
|
15
|
+
let(:run_cmd_data) { { data: [{ stdout: "abc123" }] } }
|
16
|
+
let(:command_file_name) { "my_command.bat" }
|
17
|
+
|
18
|
+
its(:winrm) { should eq(winrm_stub) }
|
19
|
+
|
20
|
+
before do
|
21
|
+
command_uploader.stub(:get_file_path).and_return("")
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#upload" do
|
25
|
+
let(:upload) { command_uploader.upload(command_string) }
|
26
|
+
|
27
|
+
it "calls winrm to upload and convert the command" do
|
28
|
+
winrm_stub.should_receive(:run_cmd).and_return(
|
29
|
+
run_cmd_data,
|
30
|
+
nil,
|
31
|
+
run_cmd_data
|
32
|
+
)
|
33
|
+
winrm_stub.should_receive(:powershell)
|
34
|
+
|
35
|
+
upload
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#command" do
|
40
|
+
subject { command }
|
41
|
+
let(:command) { command_uploader.command }
|
42
|
+
|
43
|
+
before do
|
44
|
+
command_uploader.stub command_file_name: command_file_name
|
45
|
+
end
|
46
|
+
|
47
|
+
it { should eq("cmd.exe /C #{command_file_name}") }
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#cleanup" do
|
51
|
+
subject { cleanup }
|
52
|
+
|
53
|
+
let(:cleanup) { command_uploader.cleanup }
|
54
|
+
let(:base64_file_name) { "my_base64_file" }
|
55
|
+
|
56
|
+
before do
|
57
|
+
command_uploader.stub command_file_name: command_file_name
|
58
|
+
command_uploader.stub base64_file_name: base64_file_name
|
59
|
+
end
|
60
|
+
|
61
|
+
it "cleans up the windows temp dir" do
|
62
|
+
winrm_stub.should_receive(:run_cmd).with("del #{base64_file_name} /F /Q")
|
63
|
+
winrm_stub.should_receive(:run_cmd).with("del #{command_file_name} /F /Q")
|
64
|
+
cleanup
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,41 +1,87 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Ridley::HostConnector::WinRM::Worker do
|
4
|
+
subject { winrm_worker }
|
5
|
+
let(:winrm_worker) { described_class.new(host, options) }
|
4
6
|
let(:host) { 'reset.riotgames.com' }
|
5
|
-
|
6
|
-
let(:options) do
|
7
|
-
{
|
8
|
-
winrm: {
|
9
|
-
port: 1234
|
10
|
-
}
|
11
|
-
}
|
12
|
-
end
|
7
|
+
let(:options) { {} }
|
13
8
|
|
14
|
-
|
9
|
+
before do
|
10
|
+
Ridley::HostConnector::WinRM::CommandUploader.stub(:new).and_return(double('command_uploader'))
|
11
|
+
end
|
15
12
|
|
16
13
|
describe "#winrm_port" do
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
subject(:winrm_port) { winrm_worker.winrm_port }
|
15
|
+
|
16
|
+
it { should eq(Ridley::HostConnector::DEFAULT_WINRM_PORT) }
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
context "when overridden" do
|
19
|
+
let(:options) { { winrm: { port: 1234 } } }
|
20
|
+
|
21
|
+
it { should eq(1234) }
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
25
|
describe "#winrm" do
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
subject { winrm_worker.winrm }
|
27
|
+
|
28
|
+
it { should be_a(WinRM::WinRMWebService) }
|
31
29
|
end
|
32
30
|
|
33
31
|
describe "#get_command" do
|
32
|
+
subject(:get_command) { winrm_worker.get_command(command, command_uploader_stub) }
|
33
|
+
|
34
34
|
let(:command) { "echo %TEMP%" }
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
let(:command_uploader_stub) { double('CommandUploader') }
|
36
|
+
|
37
|
+
it { should eq(command) }
|
38
|
+
|
39
|
+
context "when a command is more than 2047 characters" do
|
40
|
+
let(:command) { "a" * 2048 }
|
41
|
+
|
42
|
+
it "uploads and returns a command" do
|
43
|
+
Ridley::HostConnector::WinRM::CommandUploader.stub new: command_uploader_stub
|
44
|
+
|
45
|
+
command_uploader_stub.should_receive :upload
|
46
|
+
command_uploader_stub.stub command: "my command"
|
47
|
+
command_uploader_stub.stub(:cleanup)
|
48
|
+
|
49
|
+
get_command.should eq("my command")
|
38
50
|
end
|
39
51
|
end
|
40
52
|
end
|
53
|
+
|
54
|
+
describe "#chef_client" do
|
55
|
+
subject(:chef_client) { winrm_worker.chef_client }
|
56
|
+
|
57
|
+
it "receives a command to run chef-client" do
|
58
|
+
winrm_worker.should_receive(:run).with("chef-client")
|
59
|
+
|
60
|
+
chef_client
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#put_secret" do
|
65
|
+
subject(:put_secret) { winrm_worker.put_secret(encrypted_data_bag_secret_path) }
|
66
|
+
|
67
|
+
let(:encrypted_data_bag_secret_path) { fixtures_path.join("encrypted_data_bag_secret").to_s }
|
68
|
+
let(:secret) { File.read(encrypted_data_bag_secret_path).chomp }
|
69
|
+
|
70
|
+
it "receives a command to copy the secret" do
|
71
|
+
winrm_worker.should_receive(:run).with("echo #{secret} > C:\\chef\\encrypted_data_bag_secret")
|
72
|
+
|
73
|
+
put_secret
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#ruby_script" do
|
78
|
+
subject(:ruby_script) { winrm_worker.ruby_script(command_lines) }
|
79
|
+
let(:command_lines) { ["puts 'hello'", "puts 'there'"] }
|
80
|
+
|
81
|
+
it "receives a ruby call with the command" do
|
82
|
+
winrm_worker.should_receive(:run).with("#{described_class::EMBEDDED_RUBY_PATH} -e \"puts 'hello';puts 'there'\"")
|
83
|
+
|
84
|
+
ruby_script
|
85
|
+
end
|
86
|
+
end
|
41
87
|
end
|