Skip to content

Stateful/advanced autoresponding #369

@bitprophet

Description

@bitprophet

Spinoff of #289 but more specifically, #294. Also dovetails with #368 re: the possible need for the responses configuration being much more than a simple call/response dict.

Background

sudo (and many other prompt situations) can be handled via the naive implementation of autoresponding from #289: see string, respond with string, done.

However, in sudo's case at least, this can lead to annoying cases where an incorrect sudo password results in responding to the same prompt over and over until the remote sudoers config's passwd_tries number of tries is exceeded.

In Fabric v1, the authentication machinery was hardwired to look out for sudo's Sorry, try again response and to raise a failure flag in that case (which then affected the rest of the flow, typically by re-prompting the console user).

It would be nice to extend the autoresponse functionality so that sort of 'stateful' setup is possible, though I suspect it would be pretty complex.

Brainstorm

What's needed for the sudo use case to work better:

  • Some flag determining whether the response has been submitted & "failed" somehow; if this flag is tripped, the response machinery should probably error (or try something else, perhaps).
    • E.g. for the purposes of Context.sudo, raising an error feels nice and clean, we can catch it, bother the user, and then try again; other use cases can do whatever it is that's appropriate in their case.
    • If we did go this "stop fast/hard" route, we'd need to ensure we terminated the subprocess cleanly, probably by submitting an artificial Ctrl-C (same as when we "pass through" a real Ctrl-C).
  • In turn, that means each autoresponse key/value needs a way of recording what this failure state looks like.
    • E.g. for sudo it might simply be "a string which, if seen, indicates failure" and that's set to "Sorry, try again".
    • Could potentially be a callback or similar. In fact, the entire thing - naive and complex - could probably be recast as callbacks, with the dict key transitioning from active pattern to label.
      • Could also then use callback factories or whatever, e.g. responses['sudo'] = respond_to_prompt(trigger='sudo password:', response='mypassword', failure='Sorry, try again').
      • Where respond_to_prompt returns a callable taking as input the current value of the text stream, and a handle on the stdin writer (or which returns or yields the data to be written to stdin, if any).
      • The existing behavior could be implemented that way pretty easily, I think.
      • And the callables (again, which could be anything, not just the ones returned by builtin helper factories) could keep their own state (esp if yielding / acting as generators), which would allow for the statefulness needed for failure detection - or anything else users could want.
      • Also means each callable can do its own thing when it detects failure (and means we're not hard-coding the fact that failure detection is the only other thing in play besides regular responding). Basically, each of these is an independently tracked copy of the sudo parts of Fabric 1's io loop.
  • Going with the above-outlined callback oriented setup also means that the feature isn't even really "auto-responding" but simply a set of hooks on the stream processing.
    • Users (or core) could do literally anything with it, including the existing mirroring to stdout, "live" logging of output (though this is already possible if users submit their own stdout stream - but again, that circles back to the mirroring itself, which could stop being as much of a special case perhaps) and so on.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions