Cogito Ergo Sum
Cogito Ergo Sum

Reputation: 780

Python pexpect: Command only happens after subsequent commands are sent

Objective* I am trying to use aws ecs execute-command in conjunction with pexpect.spawn() to start an interactive bash session in a container running on an EC2 instance.

Sample Code

import sys
import time

import boto3

import pexpect
import pexpect.expect
import pexpect.replwrap



def spawn_session(container, family, cluster, ecs_client):
    print(f'Spawning Session: {container}, {family}, {cluster}')
    rsp = ecs_client.list_tasks(cluster=cluster, family=family)
    print(rsp)
    task_arn = rsp.get('taskArns')[0]
    local_argv = [
        'aws', 'ecs', 'execute-command',
        '--command', '/bin/bash',
        '--interactive', '--task', task_arn, '--container', container,
        '--cluster', cluster
    ]
    ps = pexpect.spawn(' '.join(local_argv), timeout=900, logfile=sys.stdout.buffer)
    ps.expect('ecs-execute-command.*# ', timeout=15)

    return ps

def repl_session_example(container, family, cluster, ecs_client):
    rsp = ecs_client.list_tasks(cluster=cluster, family=family)
    print(rsp)
    task_arn = rsp.get('taskArns')[0]
    # bash = pexpect.replwrap.bash()
    local_argv = [
        'aws', 'ecs', 'execute-command',
        '--command', '/bin/bash',
        '--interactive', '--task', task_arn, '--container', container,
        '--cluster', cluster
    ]
    bash = pexpect.replwrap.REPLWrapper(' '.join(local_argv), '#', None)

    return bash

def bash_session_example(container, family, cluster, ecs_client):
    rsp = ecs_client.list_tasks(cluster=cluster, family=family)
    print(rsp)
    task_arn = rsp.get('taskArns')[0]
    local_argv = [
        'aws', 'ecs', 'execute-command',
        '--command', '/bin/bash',
        '--interactive', '--task', task_arn, '--container', container,
        '--cluster', cluster
    ]
    bash = pexpect.replwrap.bash()
    bash.run_command(' '.join(local_argv))

    return bash


if __name__ == '__main__':
    ecs = boto3.client('ecs')   
    container = 'CONTAINER'
    family = 'TASK_FAMILY'
    cluster = 'CLUSTER_ARN'
    # bash = repl_session_example(container, family, cluster, ecs)
    # bash = bash_session_example(container, family, cluster, ecs)
    bash = spawn_session(container, family, cluster, ecs)
    for cmd in ['pwd', 'python --version', 'sleep 5', 'echo 2']:
        # print(f'Sending: {cmd}')
        bash.sendline(cmd)
        bash.expect('# ')
        # print(f'before: {bash.before}')
        # print(f'after: {bash.after}')
        # out = bash.run_command(cmd)
        # print(out)
    pass

Results The output seems out of order and I have to send additional input to actually get the output of the previous command.

# python temp.py 
Spawning Session: CONTAINER, FAMILY, CLUSTER

...

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.


Starting session with SessionId: ecs-execute-command-<ID_STRING>
bash-4.2# pwd     <- Sending pwd to active session
bash-4.2# python --version
pwd
/var/task/package <- Results of pwd after sending the python --version line
bash-4.2# sleep 5
python --version
Python 3.10.14    <- Version sting for python finally printed
bash-4.2# echo 2
sleep 5
echo 2            <- The 2 is not actually print out but appears reprinted

It looks like sendline() is not actually executing the command until a second command is actually sent. As a workaround I could send the command twice but it seems like I am missing something here.

Upvotes: 0

Views: 53

Answers (0)

Related Questions