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.
@@ -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.set_defualt_attribute("my_app.billing.enabled", false)
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::SSH.start}
278
+ # a hash of options to pass to the best {Ridley::HostConnector}
203
279
  #
204
- # @return [SSH::Response]
280
+ # @return [HostConnector::Response]
205
281
  def chef_client(options = {})
206
- options = client.ssh.merge(options)
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
- Ridley.log.debug "Running Chef Client on: #{self.public_hostname}"
209
- Ridley::SSH.start(self, options) do |ssh|
210
- ssh.run("sudo chef-client").first
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::SSH.start}
302
+ # a hash of options to pass to the best {Ridley::HostConnector}
220
303
  #
221
- # @return [SSH::Response, nil]
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
- options = client.ssh.merge(options)
230
- secret = File.read(client.encrypted_data_bag_secret_path).chomp
231
- command = "echo '#{secret}' > /etc/chef/encrypted_data_bag_secret; chmod 0600 /etc/chef/encrypted_data_bag_secret"
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
- Ridley.log.debug "Writing Encrypted Data Bag Secret to: #{self.public_hostname}"
234
- Ridley::SSH.start(self, options) do |ssh|
235
- ssh.run(command).first
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
 
@@ -1,3 +1,3 @@
1
1
  module Ridley
2
- VERSION = "0.10.0.rc1"
2
+ VERSION = "0.10.0.rc2"
3
3
  end
@@ -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
- it "returns an array of Bootstrapper::Contexts" do
68
- subject.contexts.should be_a(Array)
69
- subject.contexts.should each be_a(Ridley::Bootstrapper::Context)
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
- describe "ClassMethods" do
5
- let(:host) { 'reset.riotgames.com' }
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
- subject { described_class }
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
- describe "::new" do
10
- it { subject.new(host, ssh: {sudo: true}).sudo.should be_true }
11
- it { subject.new(host, ssh: {sudo: false}).sudo.should be_false }
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 SSH::ResponseSet" do
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
- subject { Ridley::HostConnector::WinRM::Worker.new(host, options) }
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
- it "can be overridden if options contains :winrm_port" do
18
- subject.winrm_port.should eq(1234)
19
- end
14
+ subject(:winrm_port) { winrm_worker.winrm_port }
15
+
16
+ it { should eq(Ridley::HostConnector::DEFAULT_WINRM_PORT) }
20
17
 
21
- it "defaults to Ridley::HostConnector::DEFAULT_WINRM_PORT when not overridden" do
22
- options.delete(:winrm)
23
- subject.winrm_port.should eq(Ridley::HostConnector::DEFAULT_WINRM_PORT)
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
- it "returns a WinRM::WinRMWebService" do
29
- subject.winrm.should be_a(WinRM::WinRMWebService)
30
- end
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
- context "when a command is less than 2047 characters" do
36
- it "returns the command" do
37
- subject.get_command(command).should eq(command)
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