-
Notifications
You must be signed in to change notification settings - Fork 253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support pty or interaction handlers #234
Conversation
Excellent looking PR @robd - I wholly support the idea of this (I'm a bit fan of |
OK great - thanks vm for the quick reply @leehambley. I will work this up with more examples and tests. I'll more carefully audit the other issues and PRs around this and try to make sure that all use cases are covered with this minimal change. |
1ceea81
to
6a86a94
Compare
@leehambley OK, I reworked this PR to add documentation and tests. I also added a If possible, I would like this to be merged before #239 since I rely on I also made some changes to the way logging works so I could log from the Note: I removed the |
@leehambley If it's OK with you, I'd like to rebase this PR on latest master - there are a couple of things that need fixing I think. Is that OK? |
Encapsulate :stdout, :stderr, :full_stdout, :full_stderr and remove duplication in local and netssh backends.
Trace logging was only partially implemented, e.g. the Pretty formatter did not support it. Move level specific log methods (error(‘whatever’), info(‘whatever’) etc from Abstract backend to Abstract formatter so they can be used by any method from SSHKit.config.output
4e2dc11
to
6c44ed2
Compare
Absolutely Rob, no history is sacred until merged 😈 |
OK, great - this is done. I fixed up some trailing whitespace and updated a new test which was failing due to changes to |
I'll need a little more time to review this sorry @robd, don't want to take the wind out of your sails :) but I'm a bit crammed on time, and I want to make sure we get it right(ish) first time. |
## Run a command which requires interaction between the client and the server | ||
|
||
on hosts do |host| | ||
execute(:passwd, :interaction_handler => MappingInteractionHandler.new( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe write this in 1.9 syntax?
Just completed a preliminary skimming through the code, and related new docs. I'll do it again when I have more time later, a couple of tiny notes, but I'll respond in depth later. Thanks for the stellar work @robd |
6c44ed2
to
3660211
Compare
Yeah no probs. I agree this would benefit from careful review, so do not On 27 April 2015 at 14:59, Lee Hambley [email protected] wrote:
|
fd25a95
to
8aeb1cf
Compare
Great PR -- Otsukaresama deshita! If it is indeed the case that requesting a PTY for a session means that the session is then associated with a pseudo-terminal for the remainder of its life and not just "for the next command I execute only," (RFC 4254?) I feel it's a service to forewarn otherwise-unsuspecting SSHKit users so that they can be wary of the attendant pitfalls. |
Hi @vivid-inc
I had to look this up! And thanks! In the end, I don't request a TBH, I don't understand whether My understanding is that Netssh opens a new ssh connection for every command, so hopefully, if a per-command pty option is needed, it will only apply to a single command, not multiple commands. I will add documentation if I add support for such an option. However, at the moment, |
Hi Rob, Well, I'll let you know how it goes for us when we get to the implementation. Thanks -- Ty C On 2015 Apr 28, at 09:45, Rob Dupuis [email protected] wrote:
|
Assigning a "pty" essentially tells the process "you are attached to a [pseudo] terminal", since we don't really have real ttys anymore. If a program thinks it is being controlled by a terminal, usually the following are expected to be true:
A more complete example might be the following:
Here, only and
(Note: the output is very prone to racing, but this is the output one should expect) http://capistranorb.com/documentation/advanced-features/ptys/ has more of the notes, and a link to the One strange caveat is that often To quote a grumpy old unix neckbeard colleague of mine:
He's not wrong, there's a lot of lost knowledge here, I'm just glad there's still enough people around writing decent terminal emulators, and keeping this stuff alive. Also, this is worth a watch - https://www.destroyallsoftware.com/talks/a-whole-new-world |
@leehambley Thanks vm for the long explanation. This is starting to make more sense. My understanding from this is whether a program requires a I guess one problem with the current implementation in this PR, is that it will not support the use case of sending input directly to a command which has no prompt. I don't know if this is a problem in the real world, and I am tempted to only add support for things when there is a real use case, so we can add tests. Also, this would require a more fundamental rework of the ssh session handling code That a whole new world video is great - thanks! |
Right, and as my colleague likes to say, since most things these days seem to be implemented by kids using scripting languages, they often don't behave correctly. i.e just because
Indeed, I'd have to check, but I'm pretty sure, for example that Final word on this about "if it doesn't prompt for input", it's poorly written, harking back to the
This is what I might call "well written", without finger pointing other programs that don't behave this way. I'm glad you enjoyed "A whole new world", I didn't enjoy the ending :) |
Support pty or interaction handlers
Merging because I just saw that you corrected the hash syntax as I suggested, thank you kindly! |
Thanks for merging this. I will try using it a bit and there may be more pty related PRs needed. You probably saw this, but I should point out I put a placeholder in the readme for the version in which
I think one of the problems is that there is a great deal of subtlety to these things, and, with pty/tty at least a lot of people only end up dealing with those issues in passing (like me!), whilst trying to get something else to work. There is a lot of incorrect or out of date information around and sometimes it is hard to build up a correct mental model of these concepts. Much of it is historical (as mentioned in that video), and can be hard to understand the evolution of the concepts.
Yes forgot to say thanks for pointing that out. Would you believe, I've only just moved off 1.8.7 on the main app I'm working on! |
Indeed, that's one of the reasons that Capistrano and SSHKit don't have communities like other Gems/etc in my opinion, we fall right in the spot between success (“I just finished a feature!”) and future success (“I just want to deploy my new feature!”), and that's the wrong time for people to run into really tricky arcane stuff that isn't wholly intuitive. We (that is my company, and some former/current Capistrano contributors) are working on a webbased, hosted CI, which can largely be thought of as a "webbased" version of Capistrano/SSHKit (i.e designed to define tasks, and triggers for those tasks, and run them in a safe, reliable, repeatable way with decent logging and integrations), vs the traditional "the tests passed" kind of CI… we're hurting for money to get to alpha, and whilst "Kickstart it from the community" is a valid argument for something like Travis with the weight of the TDD movement behind it, in spite of our best efforts using Capistrano/SSHKit can be kinda janky, and it's not (all) entirely our fault, but people generally don't like this layer, so asking for money to support us isn't exactly a compelling case. |
Gary Burnhardt's "Destroy All Software" series, which I used to be subscribed to, and have all archived has a series of excellent things, from "Refactoring some complex Ruby" down to "How does /usr/bin/time differ from the shell builtin, and how does it do magic with streams" (ever noticed how |
Is as much poking fun at the sad state of affairs we have in education, as well as language and library and package design, more than it's poking fun at any individuals, some of the common things that annoy me:
These are all really, really prevalent, and they cause issues for Capistrano, moreover things like Thor terrify me, Thor will _never_ exit with non-zero exit status, because their Aruba CLI test framework dies if the process it's testing exits non-zero… the mind simply boggles, I reported it and campaigned for it to be fixed, and it was closed as being not-worth-it! Bigger issues that tie me up, are how logging is broken, how many, many things (and SSHKit, by extension of Ruby's logger class being broken is also guilty) don't allow loggers to take two streams, |
@leehambley Sorry was out of contact - really interesting to read your thoughts here - a lot to think about. I think your point about where capistrano / sshkit sit in the workflow is completely right. I think the users are often inexperienced and short of time. Inevitably this means they skip the docs or are never going to invest enough time to understand the complexities of the issues at hand. IMHO, for this type of user (such as myself), I think the most helpful docs are the example based ones, so I will try to add at least one to the capistrano docs about interaction handlers. I think the SSHKit docs are pretty good now on this feature, so hopefully we will see fewer issues with people confused about stdin. I was thinking of adding details on how to program interaction with stdin to capistranorb.com. Do you have a feeling for where this should live? - I was thinking a new page under Getting Started or Advanced Features with a couple of examples and a link to the sshkit docs. One of the examples I would like to add is how to implement interactive sudo (which is how I got in here in the first place:). It's now very easy to implement this by redefining sudo on the capistrano dsl and providing the module DSL
def sudo(*args)
interactive_sudo_options = { interaction_handler: {
'[sudo] password for username: ' => "#{fetch(:sudo_password)}\n",
"\r\n" => nil,
} }
options = interactive_sudo_options.merge(args.extract_options!)
execute :sudo, *args, options
end
end
...
ask(:sudo_password, '', echo: false) I may have got the wrong end of the stick, but I think you may not be a fan of interactive sudo. I'm not sure if this is just the pty aspect or if you think it's wrong even without needing pty. Are you happy to include the above example about how to do interactive sudo on capistranorb.com? I think it would be useful to have some canonical instructions on this so that we can keep it up to date with any changes and also provide links from stack overflow / capistrano github issues. |
Sounds about right, maybe in the FAQ, something like "how can I use this without passwordless sudo". I'd also like to ask if you could draw the comparison to GNU
I'm a fan of solving problems at the source, the source of the "I have an automated script that needs sudo" is to grant, ergo the best place to fix it is to make I understand however that enumerating all the allowed commands in the I think our industry generally suffers from sticking bandaids over problems that originate in lower parts of the stack of which we are ignorant, but the Ruby community in particular is keen on reinventing all the things in Ruby space, personally I prefer a polyglot world where you use the right tool for the job, even if that means you have to learn 5 new tools to get the job done :) otherwise, it's all just a race to the top, and a growing base of ignorance of how things really work, that's part of the general reason that I generally prefer not to expose channels, etc through SSHKit, they belong to a lower logical level, one with an easy to exercise API. Like anything, if the implementation is good, and it solves a real problem, and it's tested and documented, the contributor has demostrated that for some subset of some people, this represents the correct way to solve the issue, and whilst I might not always be in 100% agreement, in this case I'm in favour of it, simply because the barrier from "read the Cap README" to "learn how to Ops" is quite a high one, and the risk of failure, syntax error in the sudoers file… well, it's usually fatal. At leat we all have solid configuration management though, right? |
@robd I wasn't exactly familiar with
I also checked here, and it seems there's limited, legacy, not-really-valid-anymore reasoning behind it: http://unix.stackexchange.com/a/65789/10773 |
@leehambley I will get this underway over in the https://github.com/capistrano/documentation repo.
Yes I will look more at
Yes, I will also include a note to tell users think about the pros and cons of solving this in the sudoers config vs using a password. I think all you can do is try to get peoples' interest and point out other resources for them to explore. If someone is time strapped and/or inexperienced it is likely they will end up with something that is hacked together anyway, but if we can include links to resources (eg
Yes, I agree. I hope the sort of
OK that's great - thanks. I did lock myself out of my box at one stage recently :) One other question I guess is whether you'd like to bake interactive sudo into Capistrano as a specific 'officially supported' option ask(:interactive_sudo_password, '', echo: false) # or
set(:interactive_sudo_password, load_password_from_secrets()) It sort of feels like if we are going to give this our blessing, then we could bake a high quality well tested interactive sudo interaction handler into Capistrano itself, which might avoid support overhead of people incorrectly patching the DSL. Another thing I have been thinking about, (and I think it might be a better way to handle this), is supporting interactive sudo at the SSHKit level by extending the command mapping capability. At the moment, if I understand it correctly, we support 2 mapping aspects for commands:
One approach would be to allow mapping a command to some default options. This way, adding interactive sudo support could be achieved like this: SSHKit.config.command_map.default_options[:sudo] = {
interaction_handler: InteractiveSudo.new(fetch(:sudo_password))
} This has the advantage that this would then hook other peoples' gems which use Obviously this is a bigger change than patching the capistrano DSL. |
omg, longest comment thread in the world lemme just address the important part:
I like it, good idea! |
@leehambley :) Yeah, a bit of a stream of consciousness there - sorry. Could I ask you to have a look at my updates on #242. If you are happy, I would like to get this merged before I start work on supporting default_options. |
## 1.8.1 * Change license to MIT, thanks to all the patient contributors who gave their permissions. ## 1.8.0 * add SSHKit::Backend::ConnectionPool#close_connections [PR #285](capistrano/sshkit#285) @akm * Clean up rubocop lint warnings [PR #275](capistrano/sshkit#275) @cshaffer * Prepend unused parameter names with an underscore * Prefer “safe assignment in condition” * Disambiguate regexp literals with parens * Prefer `sprintf` over `String#%` * No longer shadow `caller_line` variable in `DeprecationLogger` * Rescue `StandardError` instead of `Exception` * Remove useless `private` access modifier in `TestAbstract` * Disambiguate block operator with parens * Disambiguate between grouped expression and method params * Remove assertion in `TestHost#test_assert_hosts_compare_equal` that compares something with itself * Export environment variables and execute command in a subshell. [PR #273](capistrano/sshkit#273) @kuon * Introduce `log_command_start`, `log_command_data`, `log_command_exit` methods on `Formatter` [PR #257](capistrano/sshkit#257) @robd * Deprecate `@stdout` and `@stderr` accessors on `Command` * Add support for deprecation logging options. [README](README.md#deprecation-warnings), [PR #258](capistrano/sshkit#258) @robd * Quote environment variable values. [PR #250](capistrano/sshkit#250) @Sinjo - Chris Sinjakli * Simplified formatter hierarchy. [PR #248](capistrano/sshkit#248) @robd * `SimpleText` formatter now extends `Pretty`, rather than duplicating. * Hide ANSI color escape sequences when outputting to a file. [README](README.md#output-colors), [Issue #245](capistrano/sshkit#245), [PR #246](capistrano/sshkit#246) @robd * Now only color the output if it is associated with a tty, or the `SSHKIT_COLOR` environment variable is set. * Removed broken support for assigning an `IO` to the `output` config option. [Issue #243](capistrano/sshkit#243), [PR #244](capistrano/sshkit#244) @robd * Use `SSHKit.config.output = SSHKit::Formatter::SimpleText.new($stdin)` instead * Added support for `:interaction_handler` option on commands. [PR #234](capistrano/sshkit#234), [PR #242](capistrano/sshkit#242) @robd * Removed partially supported `TRACE` log level. [2aa7890](capistrano/sshkit@2aa7890) @robd * Add support for the `:strip` option to the `capture` method and strip by default on the `Local` backend. [PR #239](capistrano/sshkit#239), [PR #249](capistrano/sshkit#249) @robd * The `Local` backend now strips by default to be consistent with the `Netssh` one. * This reverses change [7d15a9a](capistrano/sshkit@7d15a9a) to the `Local` capture API to remove stripping by default. * If you require the raw, unstripped output, pass the `strip: false` option: `capture(:ls, strip: false)` * Simplified backend hierarchy. [PR #235](capistrano/sshkit#235), [PR #237](capistrano/sshkit#237) @robd * Moved duplicate implementations of `make`, `rake`, `test`, `capture`, `background` on to `Abstract` backend. * Backend implementations now only need to implement `execute_command`, `upload!` and `download!` * Removed `Printer` from backend hierarchy for `Local` and `Netssh` backends (they now just extend `Abstract`) * Removed unused `Net::SSH:LogLevelShim` * Removed dependency on the `colorize` gem. SSHKit now implements its own ANSI color logic, with no external dependencies. Note that SSHKit now only supports the `:bold` or plain modes. Other modes will be gracefully ignored. [#263](capistrano/sshkit#263) * New API for setting the formatter: `use_format`. This differs from `format=` in that it accepts options or arguments that will be passed to the formatter's constructor. The `format=` syntax will be deprecated in a future release. [#295](capistrano/sshkit#295) * SSHKit now immediately raises a `NameError` if you try to set a formatter that does not exist. [#295](capistrano/sshkit#295)
## 1.8.1 * Change license to MIT, thanks to all the patient contributors who gave their permissions. ## 1.8.0 * add SSHKit::Backend::ConnectionPool#close_connections [PR #285](capistrano/sshkit#285) @akm * Clean up rubocop lint warnings [PR #275](capistrano/sshkit#275) @cshaffer * Prepend unused parameter names with an underscore * Prefer “safe assignment in condition” * Disambiguate regexp literals with parens * Prefer `sprintf` over `String#%` * No longer shadow `caller_line` variable in `DeprecationLogger` * Rescue `StandardError` instead of `Exception` * Remove useless `private` access modifier in `TestAbstract` * Disambiguate block operator with parens * Disambiguate between grouped expression and method params * Remove assertion in `TestHost#test_assert_hosts_compare_equal` that compares something with itself * Export environment variables and execute command in a subshell. [PR #273](capistrano/sshkit#273) @kuon * Introduce `log_command_start`, `log_command_data`, `log_command_exit` methods on `Formatter` [PR #257](capistrano/sshkit#257) @robd * Deprecate `@stdout` and `@stderr` accessors on `Command` * Add support for deprecation logging options. [README](README.md#deprecation-warnings), [PR #258](capistrano/sshkit#258) @robd * Quote environment variable values. [PR #250](capistrano/sshkit#250) @Sinjo - Chris Sinjakli * Simplified formatter hierarchy. [PR #248](capistrano/sshkit#248) @robd * `SimpleText` formatter now extends `Pretty`, rather than duplicating. * Hide ANSI color escape sequences when outputting to a file. [README](README.md#output-colors), [Issue #245](capistrano/sshkit#245), [PR #246](capistrano/sshkit#246) @robd * Now only color the output if it is associated with a tty, or the `SSHKIT_COLOR` environment variable is set. * Removed broken support for assigning an `IO` to the `output` config option. [Issue #243](capistrano/sshkit#243), [PR #244](capistrano/sshkit#244) @robd * Use `SSHKit.config.output = SSHKit::Formatter::SimpleText.new($stdin)` instead * Added support for `:interaction_handler` option on commands. [PR #234](capistrano/sshkit#234), [PR #242](capistrano/sshkit#242) @robd * Removed partially supported `TRACE` log level. [2aa7890](capistrano/sshkit@2aa7890) @robd * Add support for the `:strip` option to the `capture` method and strip by default on the `Local` backend. [PR #239](capistrano/sshkit#239), [PR #249](capistrano/sshkit#249) @robd * The `Local` backend now strips by default to be consistent with the `Netssh` one. * This reverses change [7d15a9a](capistrano/sshkit@7d15a9a) to the `Local` capture API to remove stripping by default. * If you require the raw, unstripped output, pass the `strip: false` option: `capture(:ls, strip: false)` * Simplified backend hierarchy. [PR #235](capistrano/sshkit#235), [PR #237](capistrano/sshkit#237) @robd * Moved duplicate implementations of `make`, `rake`, `test`, `capture`, `background` on to `Abstract` backend. * Backend implementations now only need to implement `execute_command`, `upload!` and `download!` * Removed `Printer` from backend hierarchy for `Local` and `Netssh` backends (they now just extend `Abstract`) * Removed unused `Net::SSH:LogLevelShim` * Removed dependency on the `colorize` gem. SSHKit now implements its own ANSI color logic, with no external dependencies. Note that SSHKit now only supports the `:bold` or plain modes. Other modes will be gracefully ignored. [#263](capistrano/sshkit#263) * New API for setting the formatter: `use_format`. This differs from `format=` in that it accepts options or arguments that will be passed to the formatter's constructor. The `format=` syntax will be deprecated in a future release. [#295](capistrano/sshkit#295) * SSHKit now immediately raises a `NameError` if you try to set a formatter that does not exist. [#295](capistrano/sshkit#295)
I know there has been a lot of confusion and misunderstanding around the
Netssh.config.pty
option with a lot of people (myself included - sorry!) thinking that interactive sessions are supported in SSHKit. My understanding is that full interactivity support is not a direction SSHKit should go in (I agree). So this is an attempt at implementing a minimal patch to allow users to write their own programmatic, rules-based handlers to support interactive workflows. ie 'When the server responds with x', 'send y'.This PR adds support for an enhanced
:pty
option onCommand
in addition to the existing global levelNetssh.config.pty
option. This:pty
option still supports values oftrue
andfalse
/nil
as before, but also now supports passing a handler object which defineson_stdout(data, channel)
and/oron_sterr(data, channel)
. If specified, this handler is called once per line from the server and provides the channel so that data can be written in response to particular server responses.I do not propose to add any specific handlers to SSHKit - these could be provided by one or more external gems, but below is an example of a handler to support interactive sudo based on the working implementation in ssh-sudo:
As part of this work, I also formalised the mutations allowed on the
@stdout
,@stderr
,@full_stdout
and@full_stderr
attributes onCommand
, and removed the writers. I have some questions around this and ideas for a bit of further cleanup / tests here too.The test suite passes, but I didn't want to spend time adding more tests until I had some indication that this PR is desirable. As well as tests, I'd also add comprehensive docs about the interactivity support, and try to add comments to all the issues / SO questions to steer people in the right direction.
There are a number of outstanding questions, especially around local server support, but we can get into those if you're happy with the basic idea. This work was based on discussions with @kentaroi on his ssh-sudo project.
EDIT: It turns out, the changes to pty are not necessary to support the basic use case of interacting with the server, so I reworked this PR see comments below for subsequent developments