ridley-connectors 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +10 -0
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +18 -0
  7. data/Guardfile +22 -0
  8. data/LICENSE +16 -0
  9. data/README.md +107 -0
  10. data/Thorfile +39 -0
  11. data/lib/ridley-connectors/chef_objects/node_object.rb +17 -0
  12. data/lib/ridley-connectors/client.rb +54 -0
  13. data/lib/ridley-connectors/host_commander.rb +231 -0
  14. data/lib/ridley-connectors/host_connector/response.rb +27 -0
  15. data/lib/ridley-connectors/host_connector/ssh.rb +211 -0
  16. data/lib/ridley-connectors/host_connector/winrm/command_uploader.rb +87 -0
  17. data/lib/ridley-connectors/host_connector/winrm.rb +218 -0
  18. data/lib/ridley-connectors/host_connector.rb +83 -0
  19. data/lib/ridley-connectors/resources/node_resource.rb +198 -0
  20. data/lib/ridley-connectors/version.rb +5 -0
  21. data/lib/ridley-connectors.rb +11 -0
  22. data/ridley-connectors.gemspec +27 -0
  23. data/spec/fixtures/encrypted_data_bag_secret +1 -0
  24. data/spec/fixtures/my-fake.pem +27 -0
  25. data/spec/spec_helper.rb +40 -0
  26. data/spec/support/actor_mocking.rb +9 -0
  27. data/spec/support/spec_helpers.rb +20 -0
  28. data/spec/unit/ridley-connectors/chef_objects/node_object_spec.rb +22 -0
  29. data/spec/unit/ridley-connectors/client_spec.rb +32 -0
  30. data/spec/unit/ridley-connectors/host_commander_spec.rb +173 -0
  31. data/spec/unit/ridley-connectors/host_connector/ssh_spec.rb +57 -0
  32. data/spec/unit/ridley-connectors/host_connector/winrm/command_uploader_spec.rb +67 -0
  33. data/spec/unit/ridley-connectors/host_connector/winrm_spec.rb +145 -0
  34. data/spec/unit/ridley-connectors/host_connector_spec.rb +50 -0
  35. data/spec/unit/ridley-connectors/resources/node_resource_spec.rb +139 -0
  36. metadata +178 -0
@@ -0,0 +1,211 @@
1
+ require 'net/ssh'
2
+
3
+ module Ridley
4
+ module HostConnector
5
+ class SSH < HostConnector::Base
6
+ DEFAULT_PORT = 22
7
+ EMBEDDED_RUBY_PATH = '/opt/chef/embedded/bin/ruby'.freeze
8
+
9
+ # Execute a shell command on a node
10
+ #
11
+ # @param [String] host
12
+ # the host to perform the action on
13
+ # @param [String] command
14
+ #
15
+ # @option options [Hash] :ssh
16
+ # * :user (String) a shell user that will login to each node and perform the bootstrap command on
17
+ # * :password (String) the password for the shell user that will perform the bootstrap
18
+ # * :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password
19
+ # * :timeout (Float) timeout value for SSH bootstrap (5.0)
20
+ # * :sudo (Boolean) run as sudo
21
+ #
22
+ # @return [HostConnector::Response]
23
+ def run(host, command, options = {})
24
+ options = options.reverse_merge(ssh: Hash.new)
25
+ options[:ssh].reverse_merge!(port: DEFAULT_PORT, paranoid: false, sudo: false)
26
+
27
+ command = "sudo #{command}" if options[:ssh][:sudo]
28
+
29
+ Ridley::HostConnector::Response.new(host).tap do |response|
30
+ begin
31
+ log.info "Running SSH command: '#{command}' on: '#{host}' as: '#{options[:ssh][:user]}'"
32
+
33
+ defer {
34
+ Net::SSH.start(host, options[:ssh][:user], options[:ssh].slice(*Net::SSH::VALID_OPTIONS)) do |ssh|
35
+ ssh.open_channel do |channel|
36
+ if options[:sudo]
37
+ channel.request_pty do |channel, success|
38
+ unless success
39
+ raise "Could not aquire pty: A pty is required for running sudo commands."
40
+ end
41
+
42
+ channel_exec(channel, command, host, response)
43
+ end
44
+ else
45
+ channel_exec(channel, command, host, response)
46
+ end
47
+ end
48
+ ssh.loop
49
+ end
50
+ }
51
+ rescue Net::SSH::AuthenticationFailed => ex
52
+ response.exit_code = -1
53
+ response.stderr = "Authentication failure for user #{ex}"
54
+ rescue Net::SSH::ConnectionTimeout, Timeout::Error
55
+ response.exit_code = -1
56
+ response.stderr = "Connection timed out"
57
+ rescue Errno::EHOSTUNREACH
58
+ response.exit_code = -1
59
+ response.stderr = "Host unreachable"
60
+ rescue Errno::ECONNREFUSED
61
+ response.exit_code = -1
62
+ response.stderr = "Connection refused"
63
+ rescue Net::SSH::Exception => ex
64
+ response.exit_code = -1
65
+ response.stderr = ex.inspect
66
+ end
67
+
68
+ case response.exit_code
69
+ when 0
70
+ log.info "Successfully ran SSH command on: '#{host}' as: '#{options[:ssh][:user]}'"
71
+ when -1
72
+ log.info "Failed to run SSH command on: '#{host}' as: '#{options[:ssh][:user]}'"
73
+ else
74
+ log.info "Successfully ran SSH command on: '#{host}' as: '#{options[:ssh][:user]}' but it failed"
75
+ end
76
+ end
77
+ end
78
+
79
+ # Bootstrap a node
80
+ #
81
+ # @param [String] host
82
+ # the host to perform the action on
83
+ #
84
+ # @option options [Hash] :ssh
85
+ # * :user (String) a shell user that will login to each node and perform the bootstrap command on
86
+ # * :password (String) the password for the shell user that will perform the bootstrap
87
+ # * :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password
88
+ # * :timeout (Float) timeout value for SSH bootstrap (5.0)
89
+ # * :sudo (Boolean) run as sudo
90
+ #
91
+ # @return [HostConnector::Response]
92
+ def bootstrap(host, options = {})
93
+ options = options.reverse_merge(ssh: Hash.new)
94
+ options[:ssh].reverse_merge!(sudo: true, timeout: 5.0)
95
+ context = BootstrapContext::Unix.new(options)
96
+
97
+ log.info "Bootstrapping host: #{host}"
98
+ run(host, context.boot_command, options)
99
+ end
100
+
101
+ # Perform a chef client run on a node
102
+ #
103
+ # @param [String] host
104
+ # the host to perform the action on
105
+ #
106
+ # @option options [Hash] :ssh
107
+ # * :user (String) a shell user that will login to each node and perform the bootstrap command on
108
+ # * :password (String) the password for the shell user that will perform the bootstrap
109
+ # * :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password
110
+ # * :timeout (Float) timeout value for SSH bootstrap (5.0)
111
+ # * :sudo (Boolean) run as sudo
112
+ #
113
+ # @return [HostConnector::Response]
114
+ def chef_client(host, options = {})
115
+ run(host, "chef-client", options)
116
+ end
117
+
118
+ # Write your encrypted data bag secret on a node
119
+ #
120
+ # @param [String] host
121
+ # the host to perform the action on
122
+ # @param [String] secret
123
+ # your organization's encrypted data bag secret
124
+ #
125
+ # @option options [Hash] :ssh
126
+ # * :user (String) a shell user that will login to each node and perform the bootstrap command on
127
+ # * :password (String) the password for the shell user that will perform the bootstrap
128
+ # * :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password
129
+ # * :timeout (Float) timeout value for SSH bootstrap (5.0)
130
+ # * :sudo (Boolean) run as sudo
131
+ #
132
+ # @return [HostConnector::Response]
133
+ def put_secret(host, secret, options = {})
134
+ cmd = "echo '#{secret}' > /etc/chef/encrypted_data_bag_secret; chmod 0600 /etc/chef/encrypted_data_bag_secret"
135
+ run(host, cmd, options)
136
+ end
137
+
138
+ # Execute line(s) of Ruby code on a node using Chef's embedded Ruby
139
+ #
140
+ # @param [String] host
141
+ # the host to perform the action on
142
+ # @param [Array<String>] command_lines
143
+ # An Array of lines of the command to be executed
144
+ #
145
+ # @option options [Hash] :ssh
146
+ # * :user (String) a shell user that will login to each node and perform the bootstrap command on
147
+ # * :password (String) the password for the shell user that will perform the bootstrap
148
+ # * :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password
149
+ # * :timeout (Float) timeout value for SSH bootstrap (5.0)
150
+ # * :sudo (Boolean) run as sudo
151
+ #
152
+ # @return [HostConnector::Response]
153
+ def ruby_script(host, command_lines, options = {})
154
+ run(host, "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\"", options)
155
+ end
156
+
157
+ # Uninstall Chef from a node
158
+ #
159
+ # @param [String] host
160
+ # the host to perform the action on
161
+ #
162
+ # @option options [Boolena] :skip_chef (false)
163
+ # skip removal of the Chef package and the contents of the installation
164
+ # directory. Setting this to true will only remove any data and configurations
165
+ # generated by running Chef client.
166
+ # @option options [Hash] :ssh
167
+ # * :user (String) a shell user that will login to each node and perform the bootstrap command on
168
+ # * :password (String) the password for the shell user that will perform the bootstrap
169
+ # * :keys (Array, String) an array of key(s) to authenticate the ssh user with instead of a password
170
+ # * :timeout (Float) timeout value for SSH bootstrap (5.0)
171
+ # * :sudo (Boolean) run as sudo (true)
172
+ #
173
+ # @return [HostConnector::Response]
174
+ def uninstall_chef(host, options = {})
175
+ options = options.reverse_merge(ssh: Hash.new)
176
+ options[:ssh].reverse_merge!(sudo: true, timeout: 5.0)
177
+
178
+ log.info "Uninstalling Chef from host: #{host}"
179
+ run(host, CommandContext::UnixUninstall.command(options), options)
180
+ end
181
+
182
+ private
183
+
184
+ def channel_exec(channel, command, host, response)
185
+ channel.exec(command) do |ch, success|
186
+ unless success
187
+ raise "Channel execution failed while executing command #{command}"
188
+ end
189
+
190
+ channel.on_data do |ch, data|
191
+ response.stdout += data
192
+ log.info "[#{host}](SSH) #{data}" if data.present? and data != "\r\n"
193
+ end
194
+
195
+ channel.on_extended_data do |ch, type, data|
196
+ response.stderr += data
197
+ log.info "[#{host}](SSH) #{data}" if data.present? and data != "\r\n"
198
+ end
199
+
200
+ channel.on_request("exit-status") do |ch, data|
201
+ response.exit_code = data.read_long
202
+ end
203
+
204
+ channel.on_request("exit-signal") do |ch, data|
205
+ response.exit_signal = data.read_string
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,87 @@
1
+ module Ridley
2
+ module HostConnector
3
+ class WinRM
4
+ # @example
5
+ # command_uploader = CommandUploader.new(long_command, winrm)
6
+ # command_uploader.upload
7
+ # command_uploader.command
8
+ #
9
+ # This class is used by WinRM Workers when the worker is told to execute a command that
10
+ # might be too long for WinRM to handle.
11
+ #
12
+ # After an instance of this class is created, the upload method will upload the long command
13
+ # to the node being worked on in chunks. Once on the node, some Powershell code will decode
14
+ # the long_command into a batch file. The command method will return a String representing the
15
+ # command the worker will use to execute the uploaded batch script.
16
+ class CommandUploader
17
+ CHUNK_LIMIT = 1024
18
+
19
+ # @return [WinRM::WinRMWebService]
20
+ attr_reader :winrm
21
+ # @return [String]
22
+ attr_reader :base64_file_name
23
+ # @return [String]
24
+ attr_reader :command_file_name
25
+
26
+ # @param [WinRM::WinRMWebService] winrm
27
+ def initialize(winrm)
28
+ @winrm = winrm
29
+ @base64_file_name = get_file_path("winrm-upload-base64-#{unique_string}")
30
+ @command_file_name = get_file_path("winrm-upload-#{unique_string}.bat")
31
+ end
32
+
33
+ # Uploads the command encoded as base64 to a file on the host
34
+ # and then uses Powershell to transform the base64 file into the
35
+ # command that was originally passed through.
36
+ #
37
+ # @param [String] command_string
38
+ def upload(command_string)
39
+ upload_command(command_string)
40
+ convert_command
41
+ end
42
+
43
+ # @return [String] the command to execute the uploaded file
44
+ def command
45
+ "cmd.exe /C #{command_file_name}"
46
+ end
47
+
48
+ # Runs a delete command on the files generated by #base64_file_name
49
+ # and #command_file_name
50
+ def cleanup
51
+ winrm.run_cmd( "del #{base64_file_name} /F /Q" )
52
+ winrm.run_cmd( "del #{command_file_name} /F /Q" )
53
+ end
54
+
55
+ private
56
+
57
+ def upload_command(command_string)
58
+ command_string_chars(command_string).each_slice(CHUNK_LIMIT) do |chunk|
59
+ winrm.run_cmd( "echo #{chunk.join} >> \"#{base64_file_name}\"" )
60
+ end
61
+ end
62
+
63
+ def command_string_chars(command_string)
64
+ Base64.encode64(command_string).gsub("\n", '').chars.to_a
65
+ end
66
+
67
+ def convert_command
68
+ winrm.powershell <<-POWERSHELL
69
+ $base64_string = Get-Content \"#{base64_file_name}\"
70
+ $bytes = [System.Convert]::FromBase64String($base64_string)
71
+ $new_file = [System.IO.Path]::GetFullPath(\"#{command_file_name}\")
72
+ [System.IO.File]::WriteAllBytes($new_file,$bytes)
73
+ POWERSHELL
74
+ end
75
+
76
+ def unique_string
77
+ @unique_string ||= "#{Process.pid}-#{Time.now.to_i}"
78
+ end
79
+
80
+ # @return [String]
81
+ def get_file_path(file)
82
+ (winrm.run_cmd("echo %TEMP%\\#{file}"))[:data][0][:stdout].chomp
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,218 @@
1
+ # Silencing warnings because not all versions of GSSAPI support all of the GSSAPI methods
2
+ # the gssapi gem attempts to attach to and these warnings are dumped to STDERR.
3
+ silence_warnings do
4
+ require 'winrm'
5
+ end
6
+
7
+ module Ridley
8
+ module HostConnector
9
+ class WinRM < HostConnector::Base
10
+ require_relative 'winrm/command_uploader'
11
+
12
+ DEFAULT_PORT = 5985
13
+ EMBEDDED_RUBY_PATH = 'C:\opscode\chef\embedded\bin\ruby'.freeze
14
+ SESSION_TYPE_COMMAND_METHODS = {
15
+ powershell: :run_powershell_script,
16
+ cmd: :run_cmd
17
+ }.freeze
18
+
19
+ # Execute a shell command on a node
20
+ #
21
+ # @param [String] host
22
+ # the host to perform the action on
23
+ # @param [String] command
24
+ #
25
+ # @option options [Symbol] :session_type (:cmd)
26
+ # * :powershell - run the given command in a powershell session
27
+ # * :cmd - run the given command in a cmd session
28
+ # @option options [Hash] :winrm
29
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
30
+ # * :password (String) the password for the user that will perform the bootstrap (required)
31
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
32
+ #
33
+ # @return [HostConnector::Response]
34
+ def run(host, command, options = {})
35
+ options = options.reverse_merge(winrm: Hash.new, session_type: :cmd)
36
+ options[:winrm].reverse_merge!(port: DEFAULT_PORT)
37
+
38
+ command_uploaders = Array.new
39
+ user = options[:winrm][:user]
40
+ password = options[:winrm][:password]
41
+ port = options[:winrm][:port]
42
+ connection = winrm(host, port, options[:winrm].slice(:user, :password))
43
+
44
+ unless command_method = SESSION_TYPE_COMMAND_METHODS[options[:session_type]]
45
+ raise RuntimeError, "unknown session type: #{options[:session_type]}. Known session types " +
46
+ "are: #{SESSION_TYPE_COMMAND_METHODS.keys}"
47
+ end
48
+
49
+ HostConnector::Response.new(host).tap do |response|
50
+ begin
51
+ command_uploaders << command_uploader = CommandUploader.new(connection)
52
+ command = get_command(command, command_uploader)
53
+
54
+ log.info "Running WinRM Command: '#{command}' on: '#{host}' as: '#{user}'"
55
+
56
+ defer {
57
+ output = connection.send(command_method, command) do |stdout, stderr|
58
+ if stdout && stdout.present?
59
+ response.stdout += stdout
60
+ log.info "[#{host}](WinRM) #{stdout}"
61
+ end
62
+
63
+ if stderr && stderr.present?
64
+ response.stderr += stderr
65
+ log.info "[#{host}](WinRM) #{stderr}"
66
+ end
67
+ end
68
+ response.exit_code = output[:exitcode]
69
+ }
70
+ rescue ::WinRM::WinRMHTTPTransportError => ex
71
+ response.exit_code = :transport_error
72
+ response.stderr = ex.message
73
+ end
74
+
75
+ case response.exit_code
76
+ when 0
77
+ log.info "Successfully ran WinRM command on: '#{host}' as: '#{user}'"
78
+ when :transport_error
79
+ log.info "A transport error occured while attempting to run a WinRM command on: '#{host}' as: '#{user}'"
80
+ else
81
+ log.info "Successfully ran WinRM command on: '#{host}' as: '#{user}', but it failed"
82
+ end
83
+ end
84
+ ensure
85
+ begin
86
+ command_uploaders.map(&:cleanup)
87
+ rescue ::WinRM::WinRMHTTPTransportError => ex
88
+ log.info "Error cleaning up leftover Powershell scripts on some hosts"
89
+ end
90
+ end
91
+
92
+ # Returns the command if it does not break the WinRM command length
93
+ # limit. Otherwise, we return an execution of the command as a batch file.
94
+ #
95
+ # @param command [String]
96
+ #
97
+ # @return [String]
98
+ def get_command(command, command_uploader)
99
+ if command.length < CommandUploader::CHUNK_LIMIT
100
+ command
101
+ else
102
+ log.debug "Detected a command that was longer than #{CommandUploader::CHUNK_LIMIT} characters. " +
103
+ "Uploading command as a file to the host."
104
+ command_uploader.upload(command)
105
+ command_uploader.command
106
+ end
107
+ end
108
+
109
+ # Bootstrap a node
110
+ #
111
+ # @param [String] host
112
+ # the host to perform the action on
113
+ #
114
+ # @option options [Hash] :winrm
115
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
116
+ # * :password (String) the password for the user that will perform the bootstrap (required)
117
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
118
+ #
119
+ # @return [HostConnector::Response]
120
+ def bootstrap(host, options = {})
121
+ context = BootstrapContext::Windows.new(options)
122
+
123
+ log.info "Bootstrapping host: #{host}"
124
+ run(host, context.boot_command, options)
125
+ end
126
+
127
+ # Perform a chef client run on a node
128
+ #
129
+ # @param [String] host
130
+ # the host to perform the action on
131
+ #
132
+ # @option options [Hash] :winrm
133
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
134
+ # * :password (String) the password for the user that will perform the bootstrap (required)
135
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
136
+ #
137
+ # @return [HostConnector::Response]
138
+ def chef_client(host, options = {})
139
+ run(host, "chef-client", options)
140
+ end
141
+
142
+ # Write your encrypted data bag secret on a node
143
+ #
144
+ # @param [String] host
145
+ # the host to perform the action on
146
+ # @param [String] secret
147
+ # your organization's encrypted data bag secret
148
+ #
149
+ # @option options [Hash] :winrm
150
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
151
+ # * :password (String) the password for the user that will perform the bootstrap (required)
152
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
153
+ #
154
+ # @return [HostConnector::Response]
155
+ def put_secret(host, secret, options = {})
156
+ command = "echo #{secret} > C:\\chef\\encrypted_data_bag_secret"
157
+ run(host, command, options)
158
+ end
159
+
160
+ # Execute line(s) of Ruby code on a node using Chef's embedded Ruby
161
+ #
162
+ # @param [String] host
163
+ # the host to perform the action on
164
+ # @param [Array<String>] command_lines
165
+ # An Array of lines of the command to be executed
166
+ #
167
+ # @option options [Hash] :winrm
168
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
169
+ # * :password (String) the password for the user that will perform the bootstrap (required)
170
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
171
+ #
172
+ # @return [HostConnector::Response]
173
+ def ruby_script(host, command_lines, options = {})
174
+ command = "#{EMBEDDED_RUBY_PATH} -e \"#{command_lines.join(';')}\""
175
+ run(host, command, options)
176
+ end
177
+
178
+ # Uninstall Chef from a node
179
+ #
180
+ # @param [String] host
181
+ # the host to perform the action on
182
+ #
183
+ # @option options [Boolena] :skip_chef (false)
184
+ # skip removal of the Chef package and the contents of the installation
185
+ # directory. Setting this to true will only remove any data and configurations
186
+ # generated by running Chef client.
187
+ # @option options [Hash] :winrm
188
+ # * :user (String) a user that will login to each node and perform the bootstrap command on
189
+ # * :password (String) the password for the user that will perform the bootstrap (required)
190
+ # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
191
+ #
192
+ # @return [HostConnector::Response]
193
+ def uninstall_chef(host, options = {})
194
+ options[:session_type] = :powershell
195
+ log.info "Uninstalling Chef from host: #{host}"
196
+ run(host, CommandContext::WindowsUninstall.command(options), options)
197
+ end
198
+
199
+ private
200
+
201
+ # @param [String] host
202
+ # @param [Integer] port
203
+ #
204
+ # @option options [String] :user
205
+ # @option options [String] :password
206
+ #
207
+ # @return [WinRM::WinRMWebService]
208
+ def winrm(host, port, options = {})
209
+ winrm_opts = { disable_sspi: true, basic_auth_only: true }
210
+ winrm_opts[:user] = options[:user]
211
+ winrm_opts[:pass] = options[:password]
212
+ client = ::WinRM::WinRMWebService.new("http://#{host}:#{port}/wsman", :plaintext, winrm_opts)
213
+ client.set_timeout(6000)
214
+ client
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,83 @@
1
+ module Ridley
2
+ module HostConnector
3
+ class Base
4
+ include Celluloid
5
+ include Ridley::Logging
6
+
7
+ # Execute a shell command on a node
8
+ #
9
+ # @param [String] host
10
+ # the host to perform the action on
11
+ # @param [String] command
12
+ # @param [Hash] options
13
+ #
14
+ # @return [HostConnector::Response]
15
+ def run(host, command, options = {})
16
+ raise RuntimeError, "abstract function: must be implemented on includer"
17
+ end
18
+
19
+ # Bootstrap a node
20
+ #
21
+ # @param [String] host
22
+ # the host to perform the action on
23
+ # @param [Hash] options
24
+ #
25
+ # @return [HostConnector::Response]
26
+ def bootstrap(host, options = {})
27
+ raise RuntimeError, "abstract function: must be implemented on includer"
28
+ end
29
+
30
+ # Perform a chef client run on a node
31
+ #
32
+ # @param [String] host
33
+ # the host to perform the action on
34
+ # @param [Hash] options
35
+ #
36
+ # @return [HostConnector::Response]
37
+ def chef_client(host, options = {})
38
+ raise RuntimeError, "abstract function: must be implemented on includer"
39
+ end
40
+
41
+ # Write your encrypted data bag secret on a node
42
+ #
43
+ # @param [String] host
44
+ # the host to perform the action on
45
+ # @param [String] secret
46
+ # your organization's encrypted data bag secret
47
+ # @param [Hash] options
48
+ #
49
+ # @return [HostConnector::Response]
50
+ def put_secret(host, secret, options = {})
51
+ raise RuntimeError, "abstract function: must be implemented on includer"
52
+ end
53
+
54
+ # Execute line(s) of Ruby code on a node using Chef's embedded Ruby
55
+ #
56
+ # @param [String] host
57
+ # the host to perform the action on
58
+ # @param [Array<String>] command_lines
59
+ # An Array of lines of the command to be executed
60
+ # @param [Hash] options
61
+ #
62
+ # @return [HostConnector::Response]
63
+ def ruby_script(host, command_lines, options = {})
64
+ raise RuntimeError, "abstract function: must be implemented on includer"
65
+ end
66
+
67
+ # Uninstall Chef from a node
68
+ #
69
+ # @param [String] host
70
+ # the host to perform the action on
71
+ # @param [Hash] options
72
+ #
73
+ # @return [HostConnector::Response]
74
+ def uninstall_chef(host, options = {})
75
+ raise RuntimeError, "abstract function: must be implemented on includer"
76
+ end
77
+ end
78
+
79
+ require_relative 'host_connector/response'
80
+ require_relative 'host_connector/ssh'
81
+ require_relative 'host_connector/winrm'
82
+ end
83
+ end