class Chef::Knife::Bootstrap::TrainConnector
Public Class Methods
# File lib/chef/knife/bootstrap/train_connector.rb, line 37 def initialize(host_url, default_protocol, opts) @host_url = host_url @default_protocol = default_protocol @opts_in = opts end
Public Instance Methods
# File lib/chef/knife/bootstrap/train_connector.rb, line 43 def config @config ||= begin uri_opts = opts_from_uri(@host_url, @default_protocol) transport_config(@host_url, @opts_in.merge(uri_opts)) end end
Establish a connection to the configured host.
@raise [TrainError] @raise [TrainUserError]
@return [TrueClass] true if the connection could be established.
# File lib/chef/knife/bootstrap/train_connector.rb, line 67 def connect! # Force connection to establish connection.wait_until_ready true end
# File lib/chef/knife/bootstrap/train_connector.rb, line 50 def connection @connection ||= begin Train.validate_backend(config) train = Train.create(config[:backend], config) # Note that the train connection is not currently connected # to the remote host, but it's ready to go. train.connection end end
Force-deletes the file at “path” from the remote host.
@param path [String] The path of the file on the remote host
# File lib/chef/knife/bootstrap/train_connector.rb, line 168 def del_file!(path) if windows? run_command!("If (Test-Path \"#{path}\") { Remove-Item -Force -Path \"#{path}\" }") else run_command!("rm -f \"#{path}\"") end nil end
@return [String] the configured hostname
# File lib/chef/knife/bootstrap/train_connector.rb, line 75 def hostname config[:host] end
Answers the question, “Am I connected to a linux host?”
@return [Boolean] true if the connected host is linux.
# File lib/chef/knife/bootstrap/train_connector.rb, line 88 def linux? connection.platform.linux? end
normalizes path across OS's - always use forward slashes, which Windows and *nix understand.
@param path [String] The path to normalize
@return [String] the normalized path
# File lib/chef/knife/bootstrap/train_connector.rb, line 184 def normalize_path(path) path.tr("\\", "/") end
Answers the question, “is this connection configured for password auth?” @return [Boolean] true if the connection is configured with password auth
# File lib/chef/knife/bootstrap/train_connector.rb, line 81 def password_auth? config.key? :password end
Runs a command on the remote host.
@param command [String] The command to run. @param data_handler [Proc] An optional block. When provided, inbound data will be published via `data_handler.call(data)`. This can allow callers to receive and render updates from remote command execution.
@return [Train::Extras::CommandResult] an object containing stdout, stderr, and exit_status
# File lib/chef/knife/bootstrap/train_connector.rb, line 197 def run_command(command, &data_handler) connection.run_command(command, &data_handler) end
Runs a command the remote host
@param command [String] The command to run. @param data_handler [Proc] An optional block. When provided, inbound data will be published via `data_handler.call(data)`. This can allow callers to receive and render updates from remote command execution.
@raise Chef::Knife::Bootstrap::RemoteExecutionFailed
if an error occurs (non-zero exit status) @return [Train::Extras::CommandResult] an object containing stdout, stderr, and exit_status
# File lib/chef/knife/bootstrap/train_connector.rb, line 211 def run_command!(command, &data_handler) result = run_command(command, &data_handler) if result.exit_status != 0 raise RemoteExecutionFailed.new(hostname, command, result) end result end
Creates a temporary directory on the remote host if it hasn't already. Caches directory location. For *nix, it will ensure that the directory is owned by the logged-in user
@return [String] the temporary path created on the remote host.
# File lib/chef/knife/bootstrap/train_connector.rb, line 116 def temp_dir cmd = windows? ? MKTEMP_WIN_COMMAND : MKTEMP_NIX_COMMAND @tmpdir ||= begin res = run_command!(cmd) # Since pty is enabled in the connection, stderr to be merged into stdout. # So, there are cases where unnecessary multi-line output # is included before the result of mktemp. dir = res.stdout.split.last unless windows? # Ensure that dir has the correct owner. We are possibly # running with sudo right now - so this directory would be owned by root. # File upload is performed over SCP as the current logged-in user, # so we'll set ownership to ensure that works. run_command!("chown #{config[:user]} '#{dir}'") end dir end end
Answers the question, “Am I connected to a unix host?”
@note this will alwys return true for a linux host because train classifies linux as a unix
@return [Boolean] true if the connected host is unix or linux
# File lib/chef/knife/bootstrap/train_connector.rb, line 98 def unix? connection.platform.unix? end
Uploads a file from “local_path” to “remote_path”
@param local_path [String] The path to a file on the local file system @param remote_path [String] The destination path on the remote file system. @return NilClass
# File lib/chef/knife/bootstrap/train_connector.rb, line 141 def upload_file!(local_path, remote_path) connection.upload(local_path, remote_path) nil end
Uploads the provided content into the file “remote_path” on the remote host.
@param content [String] The content to upload into remote_path @param remote_path [String] The destination path on the remote file system. @return NilClass
# File lib/chef/knife/bootstrap/train_connector.rb, line 152 def upload_file_content!(content, remote_path) t = Tempfile.new("chef-content") t.binmode t << content t.close upload_file!(t.path, remote_path) nil ensure t.close t.unlink end
Answers the question, “Am I connected to a Windows host?”
@return [Boolean] true if the connected host is Windows
# File lib/chef/knife/bootstrap/train_connector.rb, line 106 def windows? connection.platform.windows? end
Private Instance Methods
This returns a hash that consists of settings populated from SSH configuration that are not already present in the configuration passed in. This is necessary because train will default these values itself - causing SSH config data to be ignored
# File lib/chef/knife/bootstrap/train_connector.rb, line 297 def missing_opts_from_ssh_config(config, opts_in) return {} unless config[:backend] == "ssh" host_cfg = ssh_config_for_host(config[:host]) opts_out = {} opts_in.each do |key, _value| if SSH_CONFIG_OVERRIDE_KEYS.include?(key) && !config.key?(key) opts_out[key] = host_cfg[key] end end opts_out end
Returns a hash containing valid options for the current transport protocol that are not already present in config
# File lib/chef/knife/bootstrap/train_connector.rb, line 266 def opts_from_caller(config, opts_in) # Train.options gives us the supported config options for the # backend provider (ssh, winrm). We'll use that # to filter out options that don't belong # to the transport type we're using. valid_opts = Train.options(config[:backend]) opts_in.select do |key, _v| valid_opts.key?(key) && !config.key?(key) end end
Extract any of username/password/host/port/transport that are in the URI and return them as a config has
# File lib/chef/knife/bootstrap/train_connector.rb, line 279 def opts_from_uri(uri, default_protocol) # Train.unpack_target_from_uri only works for complete URIs in # form of proto://[user[:pass]@]host[:port]/ # So we'll add the protocol prefix if it's not supplied. uri_to_check = if URI.regexp.match(uri) uri else "#{default_protocol}://#{uri}" end Train.unpack_target_from_uri(uri_to_check) end
Some winrm options are inferred based on other options. Return a hash of winrm options based on configuration already built.
# File lib/chef/knife/bootstrap/train_connector.rb, line 248 def opts_inferred_from_winrm(config, opts_in) return {} unless config[:backend] == "winrm" opts_out = {} if opts_in[:ssl] opts_out[:ssl] = true opts_out[:self_signed] = opts_in[:self_signed] || false end # See note here: https://github.com/mwrock/WinRM#example if %w{ssl plaintext}.include?(opts_in[:winrm_auth_method]) opts_out[:winrm_disable_sspi] = true end opts_out end
Having this as a method makes it easier to mock SSH Config
for testing.
# File lib/chef/knife/bootstrap/train_connector.rb, line 311 def ssh_config_for_host(host) require "net/ssh" unless defined?(Net::SSH) Net::SSH::Config.for(host) end
For a given url and set of options, create a config hash suitable for passing into train.
# File lib/chef/knife/bootstrap/train_connector.rb, line 223 def transport_config(host_url, opts_in) # These baseline opts are not protocol-specific opts = { target: host_url, www_form_encoded_password: true, transport_retries: 2, transport_retry_sleep: 1, backend: opts_in[:backend], logger: opts_in[:logger] } # Accepts options provided by caller if they're not already configured, # but note that they will be constrained to valid options for the backend protocol opts.merge!(opts_from_caller(opts, opts_in)) # WinRM has some additional computed options opts.merge!(opts_inferred_from_winrm(opts, opts_in)) # Now that everything is populated, fill in anything missing # that may be found in user ssh config opts.merge!(missing_opts_from_ssh_config(opts, opts_in)) Train.target_config(opts) end