"""Open a shell prompt inside an ansible-test environment.""" from __future__ import annotations import os import typing as t from ...util import ( ApplicationError, display, ) from ...config import ( ShellConfig, ) from ...executor import ( Delegate, ) from ...connections import ( Connection, LocalConnection, SshConnection, ) from ...host_profiles import ( ControllerProfile, PosixProfile, SshTargetHostProfile, ) from ...provisioning import ( prepare_profiles, ) from ...host_configs import ( ControllerConfig, OriginConfig, ) def command_shell(args): # type: (ShellConfig) -> None """Entry point for the `shell` command.""" if args.raw and isinstance(args.targets[0], ControllerConfig): raise ApplicationError('The --raw option has no effect on the controller.') host_state = prepare_profiles(args, skip_setup=args.raw) # shell if args.delegate: raise Delegate(host_state=host_state) if args.raw and not isinstance(args.controller, OriginConfig): display.warning('The --raw option will only be applied to the target.') target_profile = t.cast(SshTargetHostProfile, host_state.target_profiles[0]) if isinstance(target_profile, ControllerProfile): # run the shell locally unless a target was requested con = LocalConnection(args) # type: Connection else: # a target was requested, connect to it over SSH con = target_profile.get_controller_target_connections()[0] if isinstance(con, SshConnection) and args.raw: cmd = [] # type: t.List[str] elif isinstance(target_profile, PosixProfile): cmd = [] if args.raw: shell = 'sh' # shell required for non-ssh connection else: shell = 'bash' python = target_profile.python # make sure the python interpreter has been initialized before opening a shell display.info(f'Target Python {python.version} is at: {python.path}') optional_vars = ( 'TERM', # keep backspace working ) env = {name: os.environ[name] for name in optional_vars if name in os.environ} if env: cmd = ['/usr/bin/env'] + [f'{name}={value}' for name, value in env.items()] cmd += [shell, '-i'] else: cmd = [] con.run(cmd)