Chef and TCL/Expect

Hi,

I just recently implemented this solution and I this this just a INFO post to make the group aware this is possible.

Problem #1:
A command to install a application component requires the user to hit the RETURN key, even though supposedly the command is a “silent” installer.
AFAIK, Chef can execute a process or spawn a process but i dont know how to use chef to “send a return key” into the spawned process.

My solution (may not be the best):

  • On the VM (Linux in my case), install TCL and expect packages (yum install tcl ; yum install expect)
  • Create a .tcl script where it “expects the phrase Press enter to continue” and then presses enter for you (\r

Sample script:
File name: install.tcl (you can create this using Chef template)

package require Expect
set timeout -1
spawn ./install.sh silent
expect "Press enter to continue"
send "\r"
  • You invoke the tcl script like so…(of course you can do this via chef execute resource)
    tclsh install.tcl

Problem #2

  • The TCL script now can wait until it sees the phrase Press enter to continue and then send a return key. All good. But, the TCL script itself returns a success exit code back to Chef even though the install.sh script itself may have errored out. Not good.

Solution:

After usual googling, figured we need to add a timeout -1 so that it waits indefinitely and also add a expect eof so that it continues to wait until install.sh itself has completed fully. Next, add a catch block to catch the exit status of the spawned process (in this case install.sh) and send the exit code back to the calling application (for example, a chef execute resource)

Modified script:
File name: install.tcl

package require Expect
set timeout -1
spawn ./install.sh silent
expect "Press enter to continue"
send "\r"
expect eof
catch wait result
exit [lindex $result 3]

With this, now I can automate any command line program which requires user input on Chef, with help from TCL/Expect.

Of course, there may be a way within Chef/Ruby to send responses to command line prompts - please do let me know if there’s a simpler solution.

Regards
Aravind

1 Like

ruby’s core pty module is pretty good[1], you can use that to implement
custom resource/providers.
mixlib-shellout[2] - the library that powers most of chef’s execute and
other commands supports input passing, but its not exposed at chef DSL
level, theres an ongoing discussion to add that[3],

[1]http://docs.ruby-lang.org/en/2.0.0/PTY.html#label-Example
[2]https://github.com/chef/mixlib-shellout
[3]https://github.com/chef/chef/issues/4338

I think this is a nice case for a POC :slight_smile: . Write 12.5 style lwrp that does
this using raw ruby-pty. Can you share an example of how you would like to
use such a resource
like:

interactive_execute “example_1”

all attars of execute

interactions do
on /pattern/, 'input_string’
end
end

1 Like

Tricky one. I can see how it would be useful but it’s definitely preferable
to ask the package provider to produce a silent install or, even better,
have a custom resource that does the install.

Some thought would have to be put into how to handle multiple inputs in the
execute and if they need to be provided in order or perhaps as a hash, i.e.

interactive_execute “install annoying package” do
action :run
matchers { ‘enter username:’ => ‘user’, ‘enter password:’ => ‘pass’ }
end

1 Like

Yes, so as @yoshiwaan has pointed out, the interactive shell has to allow me the ability to wait until (unlimited number of) custom text phrases that the interactive application will ask me, and then be able to send custom keystrokes when the program actually asks for that specific detail.

TCL/Expect implements this perfectly, as far as I can tell in my brief experience with it. This combined with the ability to monitor the spawned resource's exit status means its almost as though Chef is directly talking to the spawned resource and the intermediate TCL/Expect layer complexity isn't too hard as I initially thought.

This is an idealistic scenario. Often, especially with large software shops as I am currently finding out, even if you ask for such a feature via a RFC etc. it typically takes them months to turn it around, assuming they even accept your suggestion (which I have requested in this particular case). Unfortunately, don't always have the luxury of time and need to find an alternative, even if not a long term one, and for me TCL/Expect filled that gap perfectly so far.

BTW I believe TCL/Expect is used extensively in the networking world, so I am surprised that as a Configuration Management tool, Chef doesn't offer something similar already.

Wear his NetAdmin hat Actually in a networking world (let's say: Juniper, Cisco, Checkpoint, Beeware, F5, HP, Netgear, PaloAlto, even Enterasys old hardaware, this is not an exhaustive list), if you need to use TCL/Expect, you're doing it wrong. There's another way (more or less complex) you're not aware of to push a conf, change a firewall rule or a switch port(-channel) configuration, modify a load balancer group and so on not requiring interactive operations.

Interactive operations are a bad design in our actual world as they are error prone, most installers accept an answer file.

However the use case is really interesting, and writing a specific resource for a king of limited expect within chef accepting a hash of questions/answers and, for being really interesting, able to craft a response from a previous return from a system call (TL;DR: limited set of expect commands) sounds doable and opening interesting approaches for some tasks.

(I keep that in mind, and will probably give it a try when I've time if no one did it before, maybe it can worth a RFC too)