Class: OpsWalrus::ScopedMappingInteractionHandler
- Inherits:
-
Object
- Object
- OpsWalrus::ScopedMappingInteractionHandler
- Defined in:
- lib/opswalrus/interaction_handlers.rb
Constant Summary collapse
- STANDARD_SUDO_PASSWORD_PROMPT =
/\[sudo\] password for .*?:/
- STANDARD_SSH_PASSWORD_PROMPT =
/.*?@.*?'s password:/
Instance Attribute Summary collapse
-
#input_mappings ⇒ Object
Hash[ String | Regex => (String | Proc) ].
Class Method Summary collapse
-
.mapping_for_ops_sudo_prompt(sudo_password) ⇒ Object
sudo_password : String | Nil.
-
.mapping_for_ssh_password_prompt(ssh_password) ⇒ Object
sudo_password : String | Nil.
-
.mapping_for_sudo_password(sudo_password) ⇒ Object
sudo_password : String | Nil.
Instance Method Summary collapse
-
#initialize(mappings, lookback_window_chars = DefaultLookbackWindowCharCount) ⇒ ScopedMappingInteractionHandler
constructor
A new instance of ScopedMappingInteractionHandler.
-
#on_data(_command, stream_name, data, response_channel) ⇒ Object
cmd, :stdout, data, stdin the return value from on_data is returned to Command#call_interaction_handler which is then returned verbatim to Command#on_stdout, which is then returned verbatim to the backend that called #on_stdout, and in my case that is LocalPty#handle_data_for_stdout.
-
#with_mapping(mapping = nil, sudo_password: nil, ops_sudo_password: nil, inherit_existing_mappings: true, lookback_window_chars: DefaultLookbackWindowCharCount) ⇒ Object
temporarily adds the specified input mapping to the interaction handler while the given block is being evaluated when the given block returns, then the temporary mapping is removed from the interaction handler.
Constructor Details
#initialize(mappings, lookback_window_chars = DefaultLookbackWindowCharCount) ⇒ ScopedMappingInteractionHandler
Returns a new instance of ScopedMappingInteractionHandler.
12 13 14 15 |
# File 'lib/opswalrus/interaction_handlers.rb', line 12 def initialize(mappings, lookback_window_chars = DefaultLookbackWindowCharCount) @input_mappings = mappings @online_matcher = Kleene::NaiveOnlineRegex.new(mappings.keys, lookback_window_chars) end |
Instance Attribute Details
#input_mappings ⇒ Object
Hash[ String | Regex => (String | Proc) ]
10 11 12 |
# File 'lib/opswalrus/interaction_handlers.rb', line 10 def input_mappings @input_mappings end |
Class Method Details
.mapping_for_ops_sudo_prompt(sudo_password) ⇒ Object
sudo_password : String | Nil
34 35 36 37 38 39 |
# File 'lib/opswalrus/interaction_handlers.rb', line 34 def self.mapping_for_ops_sudo_prompt(sudo_password) password_response = sudo_password && ::SSHKit::InteractionHandler::Password.new("#{sudo_password}\n") { App::LOCAL_SUDO_PASSWORD_PROMPT => password_response, } end |
.mapping_for_ssh_password_prompt(ssh_password) ⇒ Object
sudo_password : String | Nil
18 19 20 21 22 23 |
# File 'lib/opswalrus/interaction_handlers.rb', line 18 def self.mapping_for_ssh_password_prompt(ssh_password) password_response = ssh_password && ::SSHKit::InteractionHandler::Password.new("#{ssh_password}\n") { STANDARD_SSH_PASSWORD_PROMPT => password_response, } end |
.mapping_for_sudo_password(sudo_password) ⇒ Object
sudo_password : String | Nil
26 27 28 29 30 31 |
# File 'lib/opswalrus/interaction_handlers.rb', line 26 def self.mapping_for_sudo_password(sudo_password) password_response = sudo_password && ::SSHKit::InteractionHandler::Password.new("#{sudo_password}\n") { STANDARD_SUDO_PASSWORD_PROMPT => password_response, } end |
Instance Method Details
#on_data(_command, stream_name, data, response_channel) ⇒ Object
cmd, :stdout, data, stdin the return value from on_data is returned to Command#call_interaction_handler which is then returned verbatim to Command#on_stdout, which is then returned verbatim to the backend that called #on_stdout, and in my case that is LocalPty#handle_data_for_stdout. So, LocalPty#handle_data_for_stdout -> Command#on_stdout -> Command#call_interaction_handler -> ScopedMappingInteractionHandler#on_data which means that if I return that a password was emitted from this method, then back in LocalPty#handle_data_for_stdout I can discard the subsequent line that I read from stdout in order to read and immediately discard the password that this interaction handler emits.
This method returns the data that is emitted to the response channel as a result of having processed the output from a command that the interaction handler was expecting.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/opswalrus/interaction_handlers.rb', line 95 def on_data(_command, stream_name, data, response_channel) # trace(Style.yellow("regexen=#{@online_matcher.instance_exec { @regexen } }")) # trace(Style.yellow("data=`#{data}`")) # trace(Style.yellow("buffer=#{@online_matcher.instance_exec { @buffer } }")) new_matches = @online_matcher.ingest(data) # debug(Style.yellow("new_matches=`#{new_matches}`")) response_data = new_matches.find_map do |online_match| mapped_output_value = @input_mappings[online_match.regex] case mapped_output_value when Proc, Method mapped_output_value.call(online_match.match) when String mapped_output_value end end # debug(Style.yellow("response_data=`#{response_data.inspect}`")) # response_data = @input_mappings.find_map do |pattern, mapped_output_value| # pattern = pattern.is_a?(String) ? Regexp.new(Regexp.escape(pattern)) : pattern # if pattern_match = data.match(pattern) # pattern_match : MatchData | Nil # case mapped_output_value # when Proc, Method # mapped_output_value.call(pattern_match) # when String # mapped_output_value # end # end # end if response_data.nil? trace(Style.red("No interaction handler mapping for #{stream_name}: `#{data}` so no response was sent")) else debug(Style.yellow("Handling #{stream_name} message |>#{data}<|")) debug(Style.yellow("Sending response |>#{response_data}<|")) if response_channel.respond_to?(:send_data) # Net SSH Channel App.instance.trace "writing: #{response_data.to_s} to Net SSH Channel" response_channel.send_data(response_data.to_s) elsif response_channel.respond_to?(:write) # Local IO (stdin) App.instance.trace "writing: #{response_data.to_s} to pty stdin" response_channel.write(response_data.to_s) else raise "Unable to write response data to channel #{channel.inspect} - does not support '#send_data' or '#write'" end end response_data end |
#with_mapping(mapping = nil, sudo_password: nil, ops_sudo_password: nil, inherit_existing_mappings: true, lookback_window_chars: DefaultLookbackWindowCharCount) ⇒ Object
temporarily adds the specified input mapping to the interaction handler while the given block is being evaluated when the given block returns, then the temporary mapping is removed from the interaction handler
mapping : Hash[ String | Regex => (String | Proc) ] | Nil
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/opswalrus/interaction_handlers.rb', line 45 def with_mapping(mapping = nil, sudo_password: nil, ops_sudo_password: nil, inherit_existing_mappings: true, lookback_window_chars: DefaultLookbackWindowCharCount) new_mapping = inherit_existing_mappings ? @input_mappings.clone : {} if mapping raise ArgumentError.new("mapping must be a Hash") unless mapping.is_a?(Hash) new_mapping.merge!(mapping) end # ops_sudo_password takes precedence over sudo_password password_mappings = if ops_sudo_password ScopedMappingInteractionHandler.mapping_for_ops_sudo_prompt(ops_sudo_password). merge(ScopedMappingInteractionHandler.mapping_for_sudo_password(nil)) elsif sudo_password ScopedMappingInteractionHandler.mapping_for_sudo_password(sudo_password). merge(ScopedMappingInteractionHandler.mapping_for_ops_sudo_prompt(nil)) end new_mapping.merge!(password_mappings) if password_mappings # debug(Style.green("mapping: #{mapping}")) # debug(Style.green("new_mapping: #{new_mapping}")) # debug(Style.green("new_mapping.empty?: #{new_mapping.empty?}")) # debug(Style.green("new_mapping == @input_mappings: #{new_mapping == @input_mappings}")) if new_mapping.empty? || new_mapping == @input_mappings # debug(Style.red("with_mapping -> reset")) @online_matcher.reset yield self else # debug(Style.red("with_mapping -> new mapping")) yield ScopedMappingInteractionHandler.new(new_mapping, lookback_window_chars) end end |