Documentation
¶
Overview ¶
Package expect is a Go version of the classic TCL Expect.
Index ¶
- Constants
- func Continue(s *Status) func() (Tag, *Status)
- func Fail(s *Status) func() (Tag, *Status)
- func LogContinue(msg string, s *Status) func() (Tag, *Status)
- func Next() func() (Tag, *Status)
- func OK() func() (Tag, *Status)
- type BCas
- type BCasT
- type BCase
- type BExp
- type BExpT
- type BSig
- type BSnd
- type BatchRes
- type Batcher
- type Case
- type Caser
- type Code
- type Expecter
- type GExpect
- func Spawn(command string, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)
- func SpawnFake(b []Batcher, timeout time.Duration, opt ...Option) (*GExpect, <-chan error, error)
- func SpawnGeneric(opt *GenOptions, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)
- func SpawnSSH(sshClient *ssh.Client, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)
- func SpawnSSHPTY(sshClient *ssh.Client, timeout time.Duration, term term.Termios, ...) (*GExpect, <-chan error, error)
- func SpawnWithArgs(command []string, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)
- func (e *GExpect) Close() error
- func (e *GExpect) Expect(re *regexp.Regexp, timeout time.Duration) (string, []string, error)
- func (e *GExpect) ExpectBatch(batch []Batcher, timeout time.Duration) ([]BatchRes, error)
- func (e *GExpect) ExpectSwitchCase(cs []Caser, timeout time.Duration) (string, []string, int, error)
- func (e *GExpect) Options(opts ...Option) (prev Option)
- func (e *GExpect) Read(p []byte) (nr int, err error)
- func (e *GExpect) Send(in string) error
- func (e *GExpect) SendSignal(sig os.Signal) error
- func (e *GExpect) String() string
- type GenOptions
- type Option
- func BufferSize(bufferSize int) Option
- func ChangeCheck(f func() bool) Option
- func CheckDuration(d time.Duration) Option
- func DebugCheck(l *log.Logger) Option
- func NoCheck() Option
- func PartialMatch(v bool) Option
- func SendTimeout(timeout time.Duration) Option
- func SetEnv(env []string) Option
- func SetSysProcAttr(args *syscall.SysProcAttr) Option
- func Tee(w io.WriteCloser) Option
- func Verbose(v bool) Option
- func VerboseWriter(w io.Writer) Option
- type Status
- type Tag
- type TimeoutError
Examples ¶
Constants ¶
const ( // BatchSend for invoking Send in a batch BatchSend = iota // BatchExpect for invoking Expect in a batch BatchExpect // BatchSwitchCase for invoking ExpectSwitchCase in a batch BatchSwitchCase // BatchSendSignal for invoking SendSignal in a batch. BatchSendSignal )
BatchCommands.
const ( // OKTag marks the desired state was reached. OKTag = Tag(iota) // FailTag means reaching this state will fail the Switch/Case. FailTag // ContinueTag will recheck for matches. ContinueTag // NextTag skips match and continues to the next one. NextTag // NoTag signals no tag was set for this case. NoTag )
const DefaultTimeout = 60 * time.Second
DefaultTimeout is the default Expect timeout.
Variables ¶
This section is empty.
Functions ¶
func LogContinue ¶
LogContinue logs the message and returns the Continue Tag and status.
Types ¶
type BCas ¶
type BCas struct { // C holds the Caser array for the SwitchCase command. C []Caser }
BCas implements the Batcher interface for SwitchCase commands.
type BCasT ¶
type BCasT struct { // Cs holds the Caser array for the SwitchCase command. C []Caser // Tout holds the SwitchCase timeout in seconds. T int }
BCasT implements the Batcher interfacs for SwitchCase commands, adding a timeout option to the BCas type.
type BCase ¶
type BCase struct { // R contains the string regular expression. R string // S contains the string to be sent if R matches. S string // T contains the Tag. T func() (Tag, *Status) // Rt contains the number of retries. Rt int }
BCase with just a string is a bit more friendly to scripting. Implements the Caser interface.
type BExp ¶
type BExp struct { // R contains the Expect command regular expression. R string }
BExp implements the Batcher interface for Expect commands using the default timeout.
type BExpT ¶
type BExpT struct { // R contains the Expect command regular expression. R string // T holds the Expect command timeout in seconds. T int }
BExpT implements the Batcher interface for Expect commands adding a timeout option to the BExp type.
type BSnd ¶
type BSnd struct {
S string
}
BSnd implements the Batcher interface for Send commands.
type BatchRes ¶
type BatchRes struct { // Idx is used to match the result with the []Batcher commands sent in. Idx int // Out output buffer for the expect command at Batcher[Idx]. Output string // Match regexp matches for expect command at Batcher[Idx]. Match []string }
BatchRes returned from ExpectBatch for every Expect command executed.
type Batcher ¶
type Batcher interface { // cmd returns the Batch command. Cmd() int // Arg returns the command argument. Arg() string // Timeout returns the timeout duration for the command , <0 gives default value. Timeout() time.Duration // Cases returns the Caser structure for SwitchCase commands. Cases() []Caser }
Batcher interface is used to make it more straightforward and readable to create batches of Expects.
var batch = []Batcher{ &BExpT{"password",8}, &BSnd{"password\n"}, &BExp{"olakar@router>"}, &BSnd{ "show interface description\n"}, &BExp{ "olakar@router>"}, }
var batchSwCaseReplace = []Batcher{ &BCasT{[]Caser{ &BCase{`([0-9]) -- .*\(MASTER\)`, `\1` + "\n"}}, 1}, &BExp{`prompt/>`}, }
type Case ¶
type Case struct { // R is the compiled regexp to match. R *regexp.Regexp // S is the string to send if Regexp matches. S string // T is the Tag for this Case. T func() (Tag, *Status) // Rt specifies number of times to retry, only used for cases tagged with Continue. Rt int }
Case used by the ExpectSwitchCase to take different Cases. Implements the Caser interface.
type Caser ¶
type Caser interface { // RE returns a compiled regexp RE() (*regexp.Regexp, error) // Send returns the send string String() string // Tag returns the Tag. Tag() (Tag, *Status) // Retry returns true if there are retries left. Retry() bool }
Caser is an interface for ExpectSwitchCase and Batch to be able to handle both the Case struct and the more script friendly BCase struct.
type Code ¶
type Code uint32
A Code is an unsigned 32-bit error code as defined in the gRPC spec.
const ( // OK is returned on success. StatusOK Code = 0 // Canceled indicates the operation was canceled (typically by the caller). // // The gRPC framework will generate this error code when cancellation // is requested. Canceled Code = 1 // Unknown error. An example of where this error may be returned is // if a Status value received from another address space belongs to // an error-space that is not known in this address space. Also // errors raised by APIs that do not return enough error information // may be converted to this error. // // The gRPC framework will generate this error code in the above two // mentioned cases. Unknown Code = 2 // InvalidArgument indicates client specified an invalid argument. // Note that this differs from FailedPrecondition. It indicates arguments // that are problematic regardless of the state of the system // (e.g., a malformed file name). // // This error code will not be generated by the gRPC framework. InvalidArgument Code = 3 // DeadlineExceeded means operation expired before completion. // For operations that change the state of the system, this error may be // returned even if the operation has completed successfully. For // example, a successful response from a server could have been delayed // long enough for the deadline to expire. // // The gRPC framework will generate this error code when the deadline is // exceeded. DeadlineExceeded Code = 4 // NotFound means some requested entity (e.g., file or directory) was // not found. // // This error code will not be generated by the gRPC framework. NotFound Code = 5 // AlreadyExists means an attempt to create an entity failed because one // already exists. // // This error code will not be generated by the gRPC framework. AlreadyExists Code = 6 // PermissionDenied indicates the caller does not have permission to // execute the specified operation. It must not be used for rejections // caused by exhausting some resource (use ResourceExhausted // instead for those errors). It must not be // used if the caller cannot be identified (use Unauthenticated // instead for those errors). // // This error code will not be generated by the gRPC core framework, // but expect authentication middleware to use it. PermissionDenied Code = 7 // ResourceExhausted indicates some resource has been exhausted, perhaps // a per-user quota, or perhaps the entire file system is out of space. // // This error code will be generated by the gRPC framework in // out-of-memory and server overload situations, or when a message is // larger than the configured maximum size. ResourceExhausted Code = 8 // FailedPrecondition indicates operation was rejected because the // system is not in a state required for the operation's execution. // For example, directory to be deleted may be non-empty, an rmdir // operation is applied to a non-directory, etc. // // A litmus test that may help a service implementor in deciding // between FailedPrecondition, Aborted, and Unavailable: // (a) Use Unavailable if the client can retry just the failing call. // (b) Use Aborted if the client should retry at a higher-level // (e.g., restarting a read-modify-write sequence). // (c) Use FailedPrecondition if the client should not retry until // the system state has been explicitly fixed. E.g., if an "rmdir" // fails because the directory is non-empty, FailedPrecondition // should be returned since the client should not retry unless // they have first fixed up the directory by deleting files from it. // (d) Use FailedPrecondition if the client performs conditional // REST Get/Update/Delete on a resource and the resource on the // server does not match the condition. E.g., conflicting // read-modify-write on the same resource. // // This error code will not be generated by the gRPC framework. FailedPrecondition Code = 9 // Aborted indicates the operation was aborted, typically due to a // concurrency issue like sequencer check failures, transaction aborts, // etc. // // See litmus test above for deciding between FailedPrecondition, // Aborted, and Unavailable. // // This error code will not be generated by the gRPC framework. Aborted Code = 10 // OutOfRange means operation was attempted past the valid range. // E.g., seeking or reading past end of file. // // Unlike InvalidArgument, this error indicates a problem that may // be fixed if the system state changes. For example, a 32-bit file // system will generate InvalidArgument if asked to read at an // offset that is not in the range [0,2^32-1], but it will generate // OutOfRange if asked to read from an offset past the current // file size. // // There is a fair bit of overlap between FailedPrecondition and // OutOfRange. We recommend using OutOfRange (the more specific // error) when it applies so that callers who are iterating through // a space can easily look for an OutOfRange error to detect when // they are done. // // This error code will not be generated by the gRPC framework. OutOfRange Code = 11 // Unimplemented indicates operation is not implemented or not // supported/enabled in this service. // // This error code will be generated by the gRPC framework. Most // commonly, you will see this error code when a method implementation // is missing on the server. It can also be generated for unknown // compression algorithms or a disagreement as to whether an RPC should // be streaming. Unimplemented Code = 12 // Internal errors. Means some invariants expected by underlying // system has been broken. If you see one of these errors, // something is very broken. // // This error code will be generated by the gRPC framework in several // internal error conditions. Internal Code = 13 // This is a most likely a transient condition and may be corrected // by retrying with a backoff. Note that it is not always safe to retry // non-idempotent operations. // // See litmus test above for deciding between FailedPrecondition, // Aborted, and Unavailable. // // This error code will be generated by the gRPC framework during // abrupt shutdown of a server process or network connection. Unavailable Code = 14 // DataLoss indicates unrecoverable data loss or corruption. // // This error code will not be generated by the gRPC framework. DataLoss Code = 15 // Unauthenticated indicates the request does not have valid // authentication credentials for the operation. // // The gRPC framework will generate this error code when the // authentication metadata is invalid or a Credentials callback fails, // but also expect authentication middleware to generate it. Unauthenticated Code = 16 )
func (*Code) UnmarshalJSON ¶
UnmarshalJSON unmarshals b into the Code.
type Expecter ¶
type Expecter interface { // Expect reads output from a spawned session and tries matching it with the provided regular expression. // It returns all output found until match. Expect(*regexp.Regexp, time.Duration) (string, []string, error) // ExpectBatch takes an array of BatchEntries and runs through them in order. For every Expect // command a BatchRes entry is created with output buffer and sub matches. // Failure of any of the batch commands will stop the execution, returning the results up to the // failure. ExpectBatch([]Batcher, time.Duration) ([]BatchRes, error) // ExpectSwitchCase makes it possible to Expect with multiple regular expressions and actions. Returns the // full output and submatches of the commands together with an index for the matching Case. ExpectSwitchCase([]Caser, time.Duration) (string, []string, int, error) // Send sends data into the spawned session. Send(string) error // Close closes the spawned session and files. Close() error }
Expecter interface primarily to make testing easier.
type GExpect ¶
type GExpect struct {
// contains filtered or unexported fields
}
GExpect implements the Expecter interface.
func Spawn ¶
Spawn starts a new process and collects the output. The error channel returns the result of the command Spawned when it finishes. Arguments may not contain spaces.
func SpawnGeneric ¶
func SpawnGeneric(opt *GenOptions, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)
SpawnGeneric is used to write generic Spawners. It returns an Expecter. The returned channel will give the return status of the spawned session, in practice this means the return value of the provided Wait function.
func SpawnSSH ¶
func SpawnSSH(sshClient *ssh.Client, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)
SpawnSSH starts an interactive SSH session,ties it to a PTY and collects the output. The returned channel sends the state of the SSH session after it finishes.
func SpawnSSHPTY ¶
func SpawnSSHPTY(sshClient *ssh.Client, timeout time.Duration, term term.Termios, opts ...Option) (*GExpect, <-chan error, error)
SpawnSSHPTY starts an interactive SSH session and ties it to a local PTY, optionally requests a remote PTY.
func SpawnWithArgs ¶
func SpawnWithArgs(command []string, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)
SpawnWithArgs starts a new process and collects the output. The error channel returns the result of the command Spawned when it finishes. Arguments may contain spaces.
func (*GExpect) Expect ¶
Expect reads spawned processes output looking for input regular expression. Timeout set to 0 makes Expect return the current buffer. Negative timeout value sets it to Default timeout.
func (*GExpect) ExpectBatch ¶
ExpectBatch takes an array of BatchEntry and executes them in order filling in the BatchRes array for any Expect command executed.
func (*GExpect) ExpectSwitchCase ¶
func (e *GExpect) ExpectSwitchCase(cs []Caser, timeout time.Duration) (string, []string, int, error)
ExpectSwitchCase checks each Case against the accumulated out buffer, sending specified string back. Leaving Send empty will Send nothing to the process. Substring expansion can be used eg.
Case{`vf[0-9]{2}.[a-z]{3}[0-9]{2}\.net).*UP`,`show arp \1`} Given: vf11.hnd01.net UP 35 (4) 34 (4) CONNECTED 0 0/0 Would send: show arp vf11.hnd01.net
func (*GExpect) SendSignal ¶
SendSignal sends a signal to the Expect controlled process. Only works on Process Expecters.
Example ¶
ExampleGExpect_SendSignal shows the usage of the SendSignal call.
exp, r, err := Spawn("testdata/traptest.sh", 30*time.Second) if err != nil { fmt.Printf("Spawn failed: %v\n", err) return } if match, buf, err := exp.Expect(signalsInstalled, time.Second*20); err != nil { fmt.Printf("exp.Expect failed, match: %v, buf: %v, err: %v", match, buf, err) return } if err := exp.SendSignal(syscall.SIGUSR1); err != nil { fmt.Printf("exp.SendSignal failed: %v", err) return } reExpect, err := regexp.Compile("USR1") if err != nil { fmt.Printf("regexp.Compile(%q) failed: %v", "Sig USR1", err) return } match, buf, err := exp.Expect(reExpect, time.Second*20) if err != nil { fmt.Printf("exp.Expect failed, match: %v, buf: %v, err: %v", match, buf, err) return } fmt.Println(match) <-r
Output: Got the USR1 Signal
type GenOptions ¶
type GenOptions struct { // In is where Expect Send messages will be written. In io.WriteCloser // Out will be read and matched by the expecter. Out io.Reader // Wait is used by expect to know when the session is over and cleanup of io Go routines should happen. Wait func() error // Close will be called as part of the expect Close, should normally include a Close of the In WriteCloser. Close func() error // Check is called everytime a Send or Expect function is called to makes sure the session is still running. Check func() bool }
GenOptions contains the options needed to set up a generic Spawner.
type Option ¶
Option represents one Expecter option.
func BufferSize ¶
BufferSize sets the size of receive buffer in bytes.
func ChangeCheck ¶
ChangeCheck changes the Expect check function.
Example ¶
ExampleChangeCheck changes the check function runtime for an Expect session.
rIn, wIn := io.Pipe() rOut, wOut := io.Pipe() waitCh := make(chan error) outCh := make(chan string) defer close(outCh) go fakeCli(cliMap, rIn, wOut) go func() { var last string for s := range outCh { if s == last { continue } fmt.Println(s) last = s } }() exp, r, err := SpawnGeneric(&GenOptions{ In: wIn, Out: rOut, Wait: func() error { return <-waitCh }, Close: func() error { return wIn.Close() }, Check: func() bool { outCh <- "Original check" return true }}, -1) if err != nil { fmt.Printf("SpawnGeneric failed: %v\n", err) return } re := regexp.MustCompile("testrouter#") interact := func() { for cmd := range cliMap { if err := exp.Send(cmd + "\n"); err != nil { fmt.Printf("exp.Send(%q) failed: %v\n", cmd+"\n", err) return } out, _, err := exp.Expect(re, -1) if err != nil { fmt.Printf("exp.Expect(%v) failed: %v out: %v", re, err, out) return } } } interact() prev := exp.Options(ChangeCheck(func() bool { outCh <- "Replaced check" return true })) interact() exp.Options(prev) interact() waitCh <- nil exp.Close() wOut.Close() <-r
Output: Original check Replaced check Original check
func CheckDuration ¶
CheckDuration changes the default duration checking for new incoming data.
func DebugCheck ¶
DebugCheck adds logging to the check function. The check function for the spawners are called at creation/timeouts and I/O so can be usable for printing current state during debugging.
Example ¶
ExampleDebugCheck toggles the DebugCheck option.
rIn, wIn := io.Pipe() rOut, wOut := io.Pipe() rLog, wLog := io.Pipe() waitCh := make(chan error) defer rIn.Close() defer wOut.Close() defer wLog.Close() go fakeCli(cliMap, rIn, wOut) exp, r, err := SpawnGeneric(&GenOptions{ In: wIn, Out: rOut, Wait: func() error { return <-waitCh }, Close: func() error { return wIn.Close() }, Check: func() bool { return true }}, -1) if err != nil { log.Printf("SpawnGeneric failed: %v", err) return } re := regexp.MustCompile("testrouter#") interact := func() { for cmd := range cliMap { if err := exp.Send(cmd + "\n"); err != nil { log.Printf("exp.Send(%q) failed: %v\n", cmd+"\n", err) return } out, _, err := exp.Expect(re, -1) if err != nil { log.Printf("exp.Expect(%v) failed: %v out: %v", re, err, out) return } } } go func() { var last string scn := bufio.NewScanner(rLog) for scn.Scan() { ws := strings.Split(scn.Text(), " ") if ws[0] == last { continue } last = ws[0] fmt.Println(ws[0]) } }() fmt.Println("First round") interact() fmt.Println("Second round - Debugging enabled") prev := exp.Options(DebugCheck(log.New(wLog, "DebugExample ", 0))) interact() exp.Options(prev) fmt.Println("Last round - Previous Check put back") interact() waitCh <- nil exp.Close() wOut.Close() <-r
Output: First round Second round - Debugging enabled DebugExample Last round - Previous Check put back
func PartialMatch ¶
PartialMatch enables/disables the returning of unmatched buffer so that consecutive expect call works.
func SendTimeout ¶
SendTimeout set timeout for Send commands
func SetSysProcAttr ¶
func SetSysProcAttr(args *syscall.SysProcAttr) Option
SetSysProcAttr sets the SysProcAttr syscall values for the spawned process. Because this modifies cmd, it will only work with the process spawners and not effect the GExpect option method.
func Tee ¶
func Tee(w io.WriteCloser) Option
Tee duplicates all of the spawned process's output to the given writer and closes the writer when complete. Writes occur from another thread, so synchronization may be necessary.
func Verbose ¶
Verbose enables/disables verbose logging of matches and sends.
Example ¶
ExampleVerbose changes the Verbose and VerboseWriter options.
rIn, wIn := io.Pipe() rOut, wOut := io.Pipe() waitCh := make(chan error) outCh := make(chan string) defer close(outCh) go fakeCli(cliMap, rIn, wOut) go func() { var last string for s := range outCh { if s == last { continue } fmt.Println(s) last = s } }() exp, r, err := SpawnGeneric(&GenOptions{ In: wIn, Out: rOut, Wait: func() error { return <-waitCh }, Close: func() error { return wIn.Close() }, Check: func() bool { return true }}, -1, Verbose(true), VerboseWriter(os.Stdout)) if err != nil { fmt.Printf("SpawnGeneric failed: %v\n", err) return } re := regexp.MustCompile("testrouter#") var interactCmdSorted []string for k := range cliMap { interactCmdSorted = append(interactCmdSorted, k) } sort.Strings(interactCmdSorted) interact := func() { for _, cmd := range interactCmdSorted { if err := exp.Send(cmd + "\n"); err != nil { fmt.Printf("exp.Send(%q) failed: %v\n", cmd+"\n", err) return } out, _, err := exp.Expect(re, -1) if err != nil { fmt.Printf("exp.Expect(%v) failed: %v out: %v", re, err, out) return } } } interact() waitCh <- nil exp.Close() wOut.Close() <-r
Output: �[34mSent:�[39m "show system uptime\n" �[32mMatch for RE:�[39m "testrouter#" found: ["testrouter#"] Buffer: Current time: 1998-10-13 19:45:47 UTC Time Source: NTP CLOCK System booted: 1998-10-12 20:51:41 UTC (22:54:06 ago) Protocols started: 1998-10-13 19:33:45 UTC (00:12:02 ago) Last configured: 1998-10-13 19:33:45 UTC (00:12:02 ago) by abc 12:45PM up 22:54, 2 users, load averages: 0.07, 0.02, 0.01 testuser@testrouter# �[34mSent:�[39m "show system users\n" �[32mMatch for RE:�[39m "testrouter#" found: ["testrouter#"] Buffer: 7:30PM up 4 days, 2:26, 2 users, load averages: 0.07, 0.02, 0.01 USER TTY FROM LOGIN@ IDLE WHAT root d0 - Fri05PM 4days -csh (csh) blue p0 level5.company.net 7:30PM - cli testuser@testrouter# �[34mSent:�[39m "show version\n" �[32mMatch for RE:�[39m "testrouter#" found: ["testrouter#"] Buffer: Cisco IOS Software, 3600 Software (C3660-I-M), Version 12.3(4)T TAC Support: http://www.cisco.com/tac Copyright (c) 1986-2003 by Cisco Systems, Inc. Compiled Thu 18-Sep-03 15:37 by ccai ROM: System Bootstrap, Version 12.0(6r)T, RELEASE SOFTWARE (fc1) ROM: C3660-1 uptime is 1 week, 3 days, 6 hours, 41 minutes System returned to ROM by power-on System image file is "slot0:tftpboot/c3660-i-mz.123-4.T" Cisco 3660 (R527x) processor (revision 1.0) with 57344K/8192K bytes of memory. Processor board ID JAB055180FF R527x CPU at 225Mhz, Implementation 40, Rev 10.0, 2048KB L2 Cache 3660 Chassis type: ENTERPRISE 2 FastEthernet interfaces 4 Serial interfaces DRAM configuration is 64 bits wide with parity disabled. 125K bytes of NVRAM. 16384K bytes of processor board System flash (Read/Write) Flash card inserted. Reading filesystem...done. 20480K bytes of processor board PCMCIA Slot0 flash (Read/Write) Configuration register is 0x2102 testrouter#
func VerboseWriter ¶
VerboseWriter sets an alternate destination for verbose logs.
type Status ¶
type Status struct {
// contains filtered or unexported fields
}
Status contains an errormessage and a status code.
func NewStatusf ¶
NewStatusf returns a Status with the provided code and a formatted message.
type TimeoutError ¶
type TimeoutError int
TimeoutError is the error returned by all Expect functions upon timer expiry.
func (TimeoutError) Error ¶
func (t TimeoutError) Error() string
Error implements the Error interface.