ridley 0.12.4 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/lib/ridley.rb +3 -3
- data/lib/ridley/bootstrap_context.rb +100 -0
- data/lib/ridley/bootstrap_context/unix.rb +74 -0
- data/lib/ridley/bootstrap_context/windows.rb +120 -0
- data/lib/ridley/chef_objects/node_object.rb +8 -5
- data/lib/ridley/host_commander.rb +207 -0
- data/lib/ridley/host_connector.rb +49 -87
- data/lib/ridley/host_connector/ssh.rb +153 -39
- data/lib/ridley/host_connector/winrm.rb +164 -39
- data/lib/ridley/resources/node_resource.rb +52 -56
- data/lib/ridley/version.rb +1 -1
- data/ridley.gemspec +0 -2
- data/spec/spec_helper.rb +4 -4
- data/spec/support/chef_server.rb +9 -3
- data/spec/unit/ridley/{bootstrap_bindings/unix_template_binding_spec.rb → bootstrap_context/unix_spec.rb} +2 -2
- data/spec/unit/ridley/{bootstrap_bindings/windows_template_binding_spec.rb → bootstrap_context/windows_spec.rb} +2 -2
- data/spec/unit/ridley/{mixin/bootstrap_binding_spec.rb → bootstrap_context_spec.rb} +2 -6
- data/spec/unit/ridley/host_commander_spec.rb +208 -0
- data/spec/unit/ridley/host_connector/ssh_spec.rb +37 -31
- data/spec/unit/ridley/host_connector/winrm_spec.rb +124 -31
- data/spec/unit/ridley/host_connector_spec.rb +23 -147
- data/spec/unit/ridley/resources/node_resource_spec.rb +55 -115
- metadata +17 -66
- data/lib/ridley/bootstrap_bindings.rb +0 -3
- data/lib/ridley/bootstrap_bindings/unix_template_binding.rb +0 -108
- data/lib/ridley/bootstrap_bindings/windows_template_binding.rb +0 -163
- data/lib/ridley/bootstrapper.rb +0 -89
- data/lib/ridley/bootstrapper/context.rb +0 -81
- data/lib/ridley/host_connector/response_set.rb +0 -98
- data/lib/ridley/host_connector/ssh/worker.rb +0 -135
- data/lib/ridley/host_connector/winrm/worker.rb +0 -159
- data/lib/ridley/log.rb +0 -10
- data/lib/ridley/mixin/bootstrap_binding.rb +0 -77
- data/spec/unit/ridley/bootstrapper/context_spec.rb +0 -45
- data/spec/unit/ridley/bootstrapper_spec.rb +0 -96
- data/spec/unit/ridley/host_connector/response_set_spec.rb +0 -112
- data/spec/unit/ridley/host_connector/ssh/worker_spec.rb +0 -57
- data/spec/unit/ridley/host_connector/winrm/worker_spec.rb +0 -139
@@ -1,159 +0,0 @@
|
|
1
|
-
module Ridley
|
2
|
-
module HostConnector
|
3
|
-
class WinRM
|
4
|
-
# @author Kyle Allan <kallan@riotgames.com>
|
5
|
-
# @api private
|
6
|
-
class Worker
|
7
|
-
include Celluloid
|
8
|
-
include Celluloid::Logger
|
9
|
-
|
10
|
-
# @return [String]
|
11
|
-
attr_reader :user
|
12
|
-
# @return [String]
|
13
|
-
attr_reader :password
|
14
|
-
# @return [String]
|
15
|
-
attr_reader :host
|
16
|
-
# @return [Hash]
|
17
|
-
attr_reader :options
|
18
|
-
# @return [String]
|
19
|
-
attr_reader :winrm_endpoint
|
20
|
-
# @return [CommandUploader]
|
21
|
-
attr_reader :command_uploader
|
22
|
-
# @return [Array]
|
23
|
-
attr_reader :command_uploaders
|
24
|
-
|
25
|
-
finalizer :finalize_callback
|
26
|
-
|
27
|
-
EMBEDDED_RUBY_PATH = 'C:\opscode\chef\embedded\bin\ruby'.freeze
|
28
|
-
|
29
|
-
# @param host [String]
|
30
|
-
# the host the worker is going to work on
|
31
|
-
# @option options [Hash] :winrm
|
32
|
-
# * :user (String) a user that will login to each node and perform the bootstrap command on (required)
|
33
|
-
# * :password (String) the password for the user that will perform the bootstrap
|
34
|
-
# * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
|
35
|
-
def initialize(host, options = {})
|
36
|
-
options = options.deep_symbolize_keys
|
37
|
-
@options = options[:winrm] || Hash.new
|
38
|
-
@host = host
|
39
|
-
@user = @options[:user]
|
40
|
-
@password = @options[:password]
|
41
|
-
@winrm_endpoint = "http://#{host}:#{winrm_port}/wsman"
|
42
|
-
@command_uploaders = Array.new
|
43
|
-
end
|
44
|
-
|
45
|
-
def run(command)
|
46
|
-
response = Ridley::HostConnector::Response.new(host)
|
47
|
-
command_uploaders << command_uploader = CommandUploader.new(winrm)
|
48
|
-
command = get_command(command, command_uploader)
|
49
|
-
|
50
|
-
debug "Running WinRM Command: '#{command}' on: '#{host}' as: '#{user}'"
|
51
|
-
|
52
|
-
output = winrm.run_cmd(command) do |stdout, stderr|
|
53
|
-
if stdout
|
54
|
-
response.stdout += stdout
|
55
|
-
info "NODE[#{host}] #{stdout}"
|
56
|
-
end
|
57
|
-
|
58
|
-
if stderr
|
59
|
-
response.stderr += stderr unless stderr.nil?
|
60
|
-
info "NODE[#{host}] #{stdout}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
response.exit_code = output[:exitcode]
|
65
|
-
|
66
|
-
case response.exit_code
|
67
|
-
when 0
|
68
|
-
debug "Successfully ran WinRM command on: '#{host}' as: '#{user}'"
|
69
|
-
[ :ok, response ]
|
70
|
-
else
|
71
|
-
error "Successfully ran WinRM command on: '#{host}' as: '#{user}', but it failed"
|
72
|
-
error response.stdout
|
73
|
-
[ :error, response ]
|
74
|
-
end
|
75
|
-
rescue => e
|
76
|
-
error "Failed to run WinRM command on: '#{host}' as: '#{user}'"
|
77
|
-
error "#{e.class}: #{e.message}"
|
78
|
-
response.exit_code = -1
|
79
|
-
response.stderr = e.message
|
80
|
-
[ :error, response ]
|
81
|
-
end
|
82
|
-
|
83
|
-
# @return [WinRM::WinRMWebService]
|
84
|
-
def winrm
|
85
|
-
@winrm_client ||= begin
|
86
|
-
require 'active_support/core_ext/kernel/reporting'
|
87
|
-
# Silencing warnings because not all versions of GSSAPI support all of the GSSAPI methods
|
88
|
-
# the gssapi gem attempts to attach to and these warnings are dumped to STDERR.
|
89
|
-
silence_warnings do
|
90
|
-
require 'winrm'
|
91
|
-
end
|
92
|
-
|
93
|
-
client = ::WinRM::WinRMWebService.new(winrm_endpoint, :plaintext,
|
94
|
-
user: user, pass: password, disable_sspi: true, basic_auth_only: true)
|
95
|
-
client.set_timeout(6000)
|
96
|
-
client
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# @return [Fixnum]
|
101
|
-
def winrm_port
|
102
|
-
options[:port] || Ridley::HostConnector::DEFAULT_WINRM_PORT
|
103
|
-
end
|
104
|
-
|
105
|
-
# Returns the command if it does not break the WinRM command length
|
106
|
-
# limit. Otherwise, we return an execution of the command as a batch file.
|
107
|
-
#
|
108
|
-
# @param command [String]
|
109
|
-
#
|
110
|
-
# @return [String]
|
111
|
-
def get_command(command, command_uploader)
|
112
|
-
if command.length < CommandUploader::CHUNK_LIMIT
|
113
|
-
command
|
114
|
-
else
|
115
|
-
debug "Detected a command that was longer than #{CommandUploader::CHUNK_LIMIT} characters, \
|
116
|
-
uploading command as a file to the host."
|
117
|
-
command_uploader.upload(command)
|
118
|
-
command_uploader.command
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# Executes a chef-client run on the nodes
|
123
|
-
#
|
124
|
-
# @return [#run]
|
125
|
-
def chef_client
|
126
|
-
run("chef-client")
|
127
|
-
end
|
128
|
-
|
129
|
-
# Writes the given encrypted data bag secret to the node
|
130
|
-
#
|
131
|
-
# @param [String] secret
|
132
|
-
# your organization's encrypted data bag secret
|
133
|
-
#
|
134
|
-
# @return [#run]
|
135
|
-
def put_secret(secret)
|
136
|
-
command = "echo #{secret} > C:\\chef\\encrypted_data_bag_secret"
|
137
|
-
run(command)
|
138
|
-
end
|
139
|
-
|
140
|
-
# Executes a provided Ruby script in the embedded Ruby installation
|
141
|
-
#
|
142
|
-
# @param [Array<String>] command_lines
|
143
|
-
# An Array of lines of the command to be executed
|
144
|
-
#
|
145
|
-
# @return [#run]
|
146
|
-
def ruby_script(command_lines)
|
147
|
-
command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
|
148
|
-
run(command)
|
149
|
-
end
|
150
|
-
|
151
|
-
private
|
152
|
-
|
153
|
-
def finalize_callback
|
154
|
-
command_uploaders.map(&:cleanup)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
data/lib/ridley/log.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
require 'erubis'
|
2
|
-
|
3
|
-
module Ridley
|
4
|
-
module BootstrapBinding
|
5
|
-
module ClassMethods
|
6
|
-
def validate_options(options = {})
|
7
|
-
if options[:server_url].nil?
|
8
|
-
raise Errors::ArgumentError, "A server_url is required for bootstrapping"
|
9
|
-
end
|
10
|
-
|
11
|
-
if options[:validator_path].nil?
|
12
|
-
raise Errors::ArgumentError, "A path to a validator is required for bootstrapping"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# A hash of default options to be used in the Context initializer
|
17
|
-
#
|
18
|
-
# @return [Hash]
|
19
|
-
def default_options
|
20
|
-
@default_options ||= {
|
21
|
-
validator_client: "chef-validator",
|
22
|
-
attributes: Hash.new,
|
23
|
-
run_list: Array.new,
|
24
|
-
environment: "_default",
|
25
|
-
sudo: true,
|
26
|
-
hints: Hash.new
|
27
|
-
}
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class << self
|
32
|
-
def included(base)
|
33
|
-
base.extend(ClassMethods)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
attr_reader :template_file
|
38
|
-
attr_reader :bootstrap_proxy
|
39
|
-
attr_reader :chef_version
|
40
|
-
attr_reader :default_options
|
41
|
-
attr_reader :validator_path
|
42
|
-
attr_reader :encrypted_data_bag_secret
|
43
|
-
attr_reader :server_url
|
44
|
-
attr_reader :validator_client
|
45
|
-
attr_reader :node_name
|
46
|
-
attr_reader :attributes
|
47
|
-
attr_reader :run_list
|
48
|
-
attr_reader :environment
|
49
|
-
|
50
|
-
# @return [Pathname]
|
51
|
-
def templates_path
|
52
|
-
Ridley.root.join('bootstrappers')
|
53
|
-
end
|
54
|
-
|
55
|
-
# @return [String]
|
56
|
-
def first_boot
|
57
|
-
JSON.fast_generate(attributes.merge(run_list: run_list))
|
58
|
-
end
|
59
|
-
|
60
|
-
# The validation key to create a new client for the node
|
61
|
-
#
|
62
|
-
# @raise [Ridley::Errors::ValidatorNotFound]
|
63
|
-
#
|
64
|
-
# @return [String]
|
65
|
-
def validation_key
|
66
|
-
IO.read(File.expand_path(validator_path)).chomp
|
67
|
-
rescue Errno::ENOENT
|
68
|
-
raise Errors::ValidatorNotFound, "Error bootstrapping: Validator not found at '#{validator_path}'"
|
69
|
-
end
|
70
|
-
|
71
|
-
# @return [Erubis::Eruby]
|
72
|
-
def template
|
73
|
-
Erubis::Eruby.new(IO.read(template_file).chomp)
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Ridley::Bootstrapper::Context do
|
4
|
-
let(:host) { "reset.riotgames.com" }
|
5
|
-
|
6
|
-
let(:options) do
|
7
|
-
{
|
8
|
-
server_url: "https://api.opscode.com/organizations/vialstudios",
|
9
|
-
validator_client: "chef-validator",
|
10
|
-
validator_path: fixtures_path.join("reset.pem").to_s,
|
11
|
-
encrypted_data_bag_secret: File.read(fixtures_path.join("reset.pem"))
|
12
|
-
}
|
13
|
-
end
|
14
|
-
|
15
|
-
describe "ClassMethods" do
|
16
|
-
subject { Ridley::Bootstrapper::Context }
|
17
|
-
|
18
|
-
describe "::create" do
|
19
|
-
context "when the best connection is SSH" do
|
20
|
-
it "sets template_binding to a Ridley::UnixTemplateBinding" do
|
21
|
-
Ridley::HostConnector.stub(:best_connector_for).and_return(Ridley::HostConnector::SSH)
|
22
|
-
context = subject.create(host, options)
|
23
|
-
context.template_binding.should be_a(Ridley::UnixTemplateBinding)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
context "when the best connection is WinRM" do
|
28
|
-
it "sets template_binding to a Ridley::WindowsTemplateBinding" do
|
29
|
-
Ridley::HostConnector.stub(:best_connector_for).and_return(Ridley::HostConnector::WinRM)
|
30
|
-
context = subject.create(host, options)
|
31
|
-
context.template_binding.should be_a(Ridley::WindowsTemplateBinding)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context "when there is no good connection option" do
|
36
|
-
it "raises an error" do
|
37
|
-
Ridley::HostConnector.stub(:best_connector_for).and_return(nil)
|
38
|
-
expect {
|
39
|
-
context = subject.create(host, options)
|
40
|
-
}.to raise_error(Ridley::Errors::HostConnectionError)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Ridley::Bootstrapper do
|
4
|
-
let(:nodes) do
|
5
|
-
[
|
6
|
-
"33.33.33.10"
|
7
|
-
]
|
8
|
-
end
|
9
|
-
|
10
|
-
let(:options) do
|
11
|
-
{
|
12
|
-
ssh_user: "vagrant",
|
13
|
-
ssh_password: "vagrant",
|
14
|
-
server_url: "https://api.opscode.com/organizations/vialstudios",
|
15
|
-
validator_client: "vialstudios-validator",
|
16
|
-
validator_path: fixtures_path.join("reset.pem").to_s,
|
17
|
-
encrypted_data_bag_secret: File.read(fixtures_path.join("reset.pem"))
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
before(:each) { Ridley::HostConnector.stub(:best_connector_for).and_return(Ridley::HostConnector::SSH) }
|
22
|
-
|
23
|
-
describe "ClassMethods" do
|
24
|
-
subject { Ridley::Bootstrapper }
|
25
|
-
|
26
|
-
describe "::new" do
|
27
|
-
context "given a single string for nodes" do
|
28
|
-
before(:each) do
|
29
|
-
@obj = subject.new("33.33.33.10", options)
|
30
|
-
end
|
31
|
-
|
32
|
-
it "has one node" do
|
33
|
-
@obj.hosts.should have(1).item
|
34
|
-
end
|
35
|
-
|
36
|
-
it "has one context" do
|
37
|
-
@obj.contexts.should have(1).item
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context "given an an array of strings nodes" do
|
42
|
-
before(:each) do
|
43
|
-
@obj = subject.new(["33.33.33.10", "33.33.33.11"], options)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "has a host for each item given" do
|
47
|
-
@obj.hosts.should have(2).items
|
48
|
-
end
|
49
|
-
|
50
|
-
it "has a context for each item given" do
|
51
|
-
@obj.contexts.should have(2).items
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
subject { Ridley::Bootstrapper.new(nodes, options) }
|
58
|
-
|
59
|
-
describe "#hosts" do
|
60
|
-
it "returns an array of strings" do
|
61
|
-
subject.hosts.should be_a(Array)
|
62
|
-
subject.hosts.should each be_a(String)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe "#contexts" do
|
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
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
describe "#run" do
|
94
|
-
pending
|
95
|
-
end
|
96
|
-
end
|
@@ -1,112 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Ridley::HostConnector::ResponseSet do
|
4
|
-
describe "ClassMethods" do
|
5
|
-
subject { described_class }
|
6
|
-
|
7
|
-
describe "::merge!" do
|
8
|
-
let(:target) { Ridley::HostConnector::ResponseSet.new }
|
9
|
-
let(:other) { Ridley::HostConnector::ResponseSet.new }
|
10
|
-
|
11
|
-
before(:each) do
|
12
|
-
other.add_response(Ridley::HostConnector::Response.new('host.local'))
|
13
|
-
end
|
14
|
-
|
15
|
-
it "returns the mutated target" do
|
16
|
-
result = subject.merge!(target, other)
|
17
|
-
|
18
|
-
result.should eql(target)
|
19
|
-
result.should have(1).item
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
subject { described_class.new }
|
25
|
-
|
26
|
-
describe "#add_response" do
|
27
|
-
it "accepts an array of responses" do
|
28
|
-
responses = [
|
29
|
-
Ridley::HostConnector::Response.new("one.riotgames.com"),
|
30
|
-
Ridley::HostConnector::Response.new("two.riotgames.com")
|
31
|
-
]
|
32
|
-
subject.add_response(responses)
|
33
|
-
|
34
|
-
subject.responses.should have(2).items
|
35
|
-
end
|
36
|
-
|
37
|
-
it "accepts a single response" do
|
38
|
-
response = Ridley::HostConnector::Response.new("one.riotgames.com")
|
39
|
-
subject.add_response(response)
|
40
|
-
|
41
|
-
subject.responses.should have(1).item
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "#responses" do
|
46
|
-
it "returns an array of Ridley::HostConnector::Response objects including both failures and successes" do
|
47
|
-
responses = [
|
48
|
-
double('success', error?: false),
|
49
|
-
double('failure', error?: true)
|
50
|
-
]
|
51
|
-
subject.add_response(responses)
|
52
|
-
|
53
|
-
subject.responses.should have(2).items
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe "#successes" do
|
58
|
-
it "returns an array of Ridley::HostConnector::Response objects only including the successes" do
|
59
|
-
responses = [
|
60
|
-
double('success', error?: false),
|
61
|
-
double('failure', error?: true)
|
62
|
-
]
|
63
|
-
subject.add_response(responses)
|
64
|
-
|
65
|
-
subject.successes.should have(1).item
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe "#failures" do
|
70
|
-
it "returns an array of Ridley::HostConnector::Response objects only including the failures" do
|
71
|
-
responses = [
|
72
|
-
double('success', error?: false),
|
73
|
-
double('failure', error?: true)
|
74
|
-
]
|
75
|
-
subject.add_response(responses)
|
76
|
-
|
77
|
-
subject.failures.should have(1).item
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe "#merge" do
|
82
|
-
let(:target) { Ridley::HostConnector::ResponseSet.new }
|
83
|
-
let(:other) { Ridley::HostConnector::ResponseSet.new }
|
84
|
-
|
85
|
-
before(:each) do
|
86
|
-
other.add_response(Ridley::HostConnector::Response.new('host.local'))
|
87
|
-
end
|
88
|
-
|
89
|
-
it "returns a new Ridley::HostConnector::ResponseSet object" do
|
90
|
-
result = target.merge(other)
|
91
|
-
|
92
|
-
result.should have(1).item
|
93
|
-
target.should have(0).items
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
describe "#merge!" do
|
98
|
-
let(:target) { Ridley::HostConnector::ResponseSet.new }
|
99
|
-
let(:other) { Ridley::HostConnector::ResponseSet.new }
|
100
|
-
|
101
|
-
before(:each) do
|
102
|
-
other.add_response(Ridley::HostConnector::Response.new('host.local'))
|
103
|
-
end
|
104
|
-
|
105
|
-
it "returns the mutated target" do
|
106
|
-
result = target.merge!(other)
|
107
|
-
|
108
|
-
result.should have(1).item
|
109
|
-
target.should have(1).item
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|