rust-cli / rexpect

.github/workflows/ci.yml
https://docs.rs/rexpect
MIT License
328 stars 56 forks source link

Need help on converting a bash script into rexpect #5

Closed tjyang closed 6 years ago

tjyang commented 6 years ago

Hi @philippkeller

#realm join -v --computer-ou="ou=servers,dc=test,dc=com" test.com -U admin_ad
 * Resolving: _ldap._tcp.test.com
 * Performing LDAP DSE lookup on: 100.76.1.18
 * Performing LDAP DSE lookup on: 100.76.1.19
 * Performing LDAP DSE lookup on: 100.64.0.65
 * Successfully discovered: test.com
Password for admin_ad: admin_ad_password
realm join -v --computer-ou="ou=ma,ou=servers,dc=test,dc=com" test.com -U admin_ad << MYPASSWD
admin_ad_password
MYPASSWD
//Run this code with cargo
// Join test.com AD domain on CentOS 7 with SSSD enabled.
//  Following is the response from realm command
//!  realm join -v --computer-ou="ou=unit1,ou=servers,dc=test,dc=com" test.com -U admin_ad
//!  * Resolving: _ldap._tcp.test.com
//!  * Performing LDAP DSE lookup on: 100.76.1.18
//!  * Performing LDAP DSE lookup on: 100.76.1.19
//!  * Performing LDAP DSE lookup on: 100.58.0.65
//!  * Successfully discovered: test.com
//! Password for admin_ad:

extern crate rexpect;

use rexpect::spawn;
use rexpect::errors::*;

fn  join() -> Result<()> {
    let mut p = spawn("realm join -v --computer-ou=\"ou=ma,ou=servers,dc=test,dc=com\" test.com -U admin_ad", Some(30_000))?;
    p.exp_regex("^Password for .*: ")?;
    p.send_line("admin_ad_password")?;
    p.exp_eof()?;
    Ok(())
}

fn main() {
    join().unwrap_or_else(|e| panic!("Failed to joing test.com AD domain {}", e));
}
>target/debug/joinad
thread 'main ' panicked at 'Failed to joing test.com AD domain EOF (End of File): Expected Regex: "^Password for .*: " but got EOF after reading " * Resolving: _ldap._tcp.test.com
philippkeller commented 6 years ago

the problem is that p.exp_regex("^Password for .*: ")?; will try to match Password at the beginning of the not-yet-consumed output, not the line: That means you try to match ^Password on the string starting with * Resolving: _ldap._tcp.test.com.

You can remove the ^ or replace it with \n

tjyang commented 6 years ago

@philippkeller , Thanks for the explanation about not-yet-consumed output string starting with " Resolving ..." and ended with "Password for .: ".

philippkeller commented 6 years ago

marvellous! So it worked then?

philippkeller commented 6 years ago

@tjyang also thanks to your issue I now added a note about matching beginning of line to the documentation

tjyang commented 6 years ago

Thanks for the link to method.exp_regex documentation.

Currently the command just ended without error message after I replace "^" with "\n" as you suggested.

Is there a method to print out the output "realm join .." for debugging/informational purpose ?

I like to print output before " p.exp_regex("\nPassword " line. ...

philippkeller commented 6 years ago

yes, exp_regex outputs a tuple:

  1. the yet unread output
  2. the matched regex

So the realm join message should be in the first part. You might need regex to extract the piece you're interested in

tjyang commented 6 years ago
realm: Couldn't join realm: Not authorized to perform this action
philippkeller commented 6 years ago

you can check for the exit code like this:

match p.process.wait() {
    Ok(wait::WaitStatus::Exited(_, 0)) => println!("realm exited with code 0, all good!"),
    _ => println!("realm exited with code >0, or it was killed"),
}

I also added a full example with how to handle exit codes here.

Thanks for these questions! Keep 'em coming as it helps me to extend the documentation

tjyang commented 6 years ago

Thanks for the p.process.wait pointer .

How can I print out the error message from stderr (2) once exit code is not zero ?

philippkeller commented 6 years ago

p.exp_eof()? returns all the not yet consumed output (that is stdout and stderr combined, because in tty you combine those two outputs, as in the shell you see them both together). In your example this would include the error message of realm.

I extended this example (see line 23 and following) to show how that would be used in context.

tjyang commented 6 years ago

@philippkeller

Thanks for great help so far. I will switch from golang-gexpect to rust-rexpect for AD join/leave operations from linux.

Following is the expect output from cargo run.

<snipped>
    Finished dev [unoptimized + debuginfo] target(s) in 2.43 secs
     Running `target/debug/ad`
 realm join  failed with exit code 1
Output (stdout and stderr):
realm: Couldn't join realm: Not authorized to perform this action
philippkeller commented 6 years ago

glad to hear that :) Judging from the output it seems that your program is working, the issue is only with the user permissions, correct?

tjyang commented 6 years ago

I will post a follow up once I have my rust A.D. binary working to the way I need. like adding command line argument processing to use default ad admin account if no one provided. Sigh, too many compute languages to learn in sysadmin life.