Class: ElasticBeans::Command::Exec
- Inherits:
-
Object
- Object
- ElasticBeans::Command::Exec
- Defined in:
- lib/elastic_beans/command/exec.rb
Overview
:nodoc: all
Defined Under Namespace
Classes: BastionAuthenticationError, ExecEnvironmentBusyError, TerminatedInstanceError
Constant Summary collapse
- USAGE =
"exec COMMAND STRING"
- DESC =
"Run an arbitrary command in the context of your application"
- LONG_DESC =
<<-LONG_DESC Run an arbitrary command in the context of your application. The command is run in an "exec" environment, separate from your webserver or worker environments. You must create the exec environment prior to this command being run: `beans create exec -a APPLICATION`. Output from the command is appended to /var/log/elastic_beans/exec/command.log. If interactive, runs the command via SSH, tunneling through a bastion server if specified. Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. LONG_DESC
- COMMAND_LOGFILE =
:category: internal
"/var/log/elastic_beans/exec/command.log"
- COMMAND_SCRIPT =
:category: internal
"/opt/elastic_beans/exec/run_command.sh"
- TAKEN_WAIT_PERIOD =
:category: internal
5
- TAKEN_WAIT_TIMEOUT =
:category: internal
120
Instance Method Summary collapse
-
#initialize(application:, bastion_host:, bastion_identity_file:, bastion_username:, identity_file:, interactive:, username:, ec2:, ui:) ⇒ Exec
constructor
A new instance of Exec.
- #run(*command_parts) ⇒ Object
Constructor Details
#initialize(application:, bastion_host:, bastion_identity_file:, bastion_username:, identity_file:, interactive:, username:, ec2:, ui:) ⇒ Exec
Returns a new instance of Exec.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/elastic_beans/command/exec.rb', line 29 def initialize( application:, bastion_host:, bastion_identity_file:, bastion_username:, identity_file:, interactive:, username:, ec2:, ui: ) @application = application @bastion_host = bastion_host @bastion_identity_file = bastion_identity_file @bastion_username = bastion_username @identity_file = identity_file @interactive = interactive @username = username || "ec2-user" @ec2 = ec2 @ui = ui end |
Instance Method Details
#run(*command_parts) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/elastic_beans/command/exec.rb', line 51 def run(*command_parts) command = command_from_parts(command_parts) ui.info("Running `#{command.command_string}' on #{application.name}... (ID=#{command.id})") if interactive? ui.info("Finding an exec instance...") freeze_command = ::ElasticBeans::Exec::Command.freeze_instance begin ui.debug { "Freezing exec instance... (ID=#{freeze_command.id})" } application.enqueue_command(freeze_command) application.register_command(command) freeze_command = wait_until_command_taken(freeze_command) ui.debug { "Frozen exec instance: '#{freeze_command.instance_id}'" } command.instance_id = freeze_command.instance_id command.start_time = freeze_command.start_time application.register_command(command) begin instance_ip = ec2.describe_instances(instance_ids: [freeze_command.instance_id]).reservations[0].instances[0].private_ip_address rescue ::Aws::EC2::Errors::InvalidInstanceIDMalformed raise TerminatedInstanceError.new(instance_id: freeze_command.instance_id) end # It's possible a retry would just work, but I'd like to see this happen in reality before I assume that. if instance_ip.nil? raise TerminatedInstanceError.new(instance_id: freeze_command.instance_id) end ui.info("Connecting to #{username}@#{instance_ip}...") ElasticBeans::SSH.new( hostname: instance_ip, username: username, identity_file: identity_file, bastion_host: bastion_host, bastion_username: bastion_username, bastion_identity_file: bastion_identity_file, ssh_options: %w(-t), command: [ # Do not lose command exit status *%w(set -o pipefail &&), # Log command start just like SQSConsumer "echo", %("Executing command ID=#{command.id} \\`#{command.command_string}' on host `hostname` pid $$..."), ">>", COMMAND_LOGFILE, "&&", # Load application environment "sudo", COMMAND_SCRIPT, *command_parts, # Log command execution "|", "tee", "-a", COMMAND_LOGFILE, ";", # Save command exit status for final exit *%w(status=$? ;), # Log command end just like SQSConsumer "echo", %("Command ID=#{command.id} \\`#{command.command_string}' exited on host `hostname`: pid $$ exit $status"), ">>", COMMAND_LOGFILE, "&&", # Propagate command exit status *%w(exit $status), ], logger: ui, ).connect rescue ElasticBeans::SSH::BastionAuthenticationError => e raise BastionAuthenticationError.new(cause: e) ensure begin ui.info("Cleaning up, please do not interrupt!") application.kill_command(freeze_command) application.deregister_command(command) rescue Interrupt retry end end else application.enqueue_command(command) end end |