Skip to content

Commit

Permalink
Remove UUID from instance names (#11)
Browse files Browse the repository at this point in the history
* Remove UUID from instance name

* Replace print with appropriate logging

* Update documentation

* Fix typing and docstrings
  • Loading branch information
jawang35 authored Jul 8, 2019
1 parent 639a76e commit 6bd2358
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 45 deletions.
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source activate run-on-ec2
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ $ cp .env.sample .env # Fill out .env file
$ docker pull surfline/run-on-ec2
$ docker run --rm -it --env-file=.env surfline/run-on-ec2 echo \"hello world\"

Creating key pair run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Launching instance run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Waiting for instance run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce to be ready...
Running command "echo \"hello world\"" on 10.128.130.201...
Creating key pair run-on-ec2-5f8b4f4b-1a2b-45d5-9685-57f9d2794f9d...
Launching instance run-on-ec2...
Waiting for instance i-049278b0da448e7a2 to be ready...
Waiting for SSH to become available on 10.128.130.130...
Running command "echo "hello world "" on 10.128.130.130...
hello world
Terminating instance run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Deleting key pair run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Closing SSH connection to 10.128.130.130...
Terminating instance i-049278b0da448e7a2...
Deleting key pair run-on-ec2-5f8b4f4b-1a2b-45d5-9685-57f9d2794f9d...
```

## Development
Expand All @@ -48,13 +50,15 @@ $ source activate run-on-ec2

```sh
$ cp .env.sample .env # Fill out .env file
$ env $(cat .env | xargs) python main.py echo \"hello world\"
$ env $(xargs < .env) python main.py echo \"hello world\"

Creating key pair run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Launching instance run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Waiting for instance run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce to be ready...
Running command "echo \"hello world\"" on 10.128.130.201...
Creating key pair run-on-ec2-5f8b4f4b-1a2b-45d5-9685-57f9d2794f9d...
Launching instance run-on-ec2...
Waiting for instance i-049278b0da448e7a2 to be ready...
Waiting for SSH to become available on 10.128.130.130...
Running command "echo "hello world "" on 10.128.130.130...
hello world
Terminating instance run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Deleting key pair run-on-ec2-eb5f9910-1635-40e1-b120-0e08b06a60ce...
Closing SSH connection to 10.128.130.130...
Terminating instance i-049278b0da448e7a2...
Deleting key pair run-on-ec2-5f8b4f4b-1a2b-45d5-9685-57f9d2794f9d...
```
48 changes: 30 additions & 18 deletions lib/ec2.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import logging
import sys

import boto3

EC2_CLIENT = boto3.client('ec2')
EC2_RESOURCE = boto3.resource('ec2')

logger = logging.getLogger('run-on-ec2')


class TempKeyPair:
"""
Expand All @@ -13,22 +17,22 @@ class TempKeyPair:
On exit the Key Pair is deleted.
:param name: Name of Key Pair to create.
:type name: str
Arguments:
key_name: Name of Key Pair to create.
"""

def __init__(self, name):
self.name = name
def __init__(self, key_name: str):
self.key_name = key_name

def __enter__(self):
print(f'Creating key pair {self.name}...')
key_pair = EC2_CLIENT.create_key_pair(KeyName=self.name)
logger.info(f'Creating key pair {self.key_name}...')
key_pair = EC2_CLIENT.create_key_pair(KeyName=self.key_name)
pem = key_pair['KeyMaterial']
return pem

def __exit__(self, type, value, traceback):
print(f'Deleting key pair {self.name}...')
EC2_CLIENT.delete_key_pair(KeyName=self.name)
logger.info(f'Deleting key pair {self.key_name}...')
EC2_CLIENT.delete_key_pair(KeyName=self.key_name)


class TempInstance():
Expand All @@ -40,24 +44,30 @@ class TempInstance():
On exit the Instance is terminated.
:param name: Name to tag Instance with.
:type name: str
:param launch_template_name: Name of launch template to launch Instance with.
:type launch_template_name: str
:param subnet_id: ID for subnet to launch Instance in.
:type subnet_id: str
Arguments:
name: Name to tag Instance with.
launch_template_name: Name of launch template to launch Instance with.
key_name: Name of Key Pair to associate Instance with.
subnet_id: ID for subnet to launch Instance in.
"""

def __init__(self, name, launch_template_name, subnet_id):
def __init__(
self,
name: str,
launch_template_name: str,
key_name: str,
subnet_id: str,
):
self.name = name
self.launch_template_name = launch_template_name
self.key_name = key_name
self.subnet_id = subnet_id

def __enter__(self):
print(f'Launching instance {self.name}...')
logger.info(f'Launching instance {self.name}...')
self.instance = EC2_RESOURCE.create_instances(
LaunchTemplate={'LaunchTemplateName': self.launch_template_name},
KeyName=self.name,
KeyName=self.key_name,
MinCount=1,
MaxCount=1,
SubnetId=self.subnet_id,
Expand All @@ -73,6 +83,8 @@ def __enter__(self):
},
],
)[0]
logger.info(f'Waiting for instance {self.instance.instance_id} to be '
'ready...')

try:
self.instance.wait_until_running()
Expand All @@ -82,5 +94,5 @@ def __enter__(self):
raise

def __exit__(self, type, value, traceback):
print(f'Terminating instance {self.name}...')
logger.info(f'Terminating instance {self.instance.instance_id}...')
self.instance.terminate()
20 changes: 11 additions & 9 deletions lib/ssh.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from io import StringIO
import logging
from time import sleep
from timeit import default_timer

from fabric import Connection
from paramiko import RSAKey
from paramiko.ssh_exception import NoValidConnectionsError

logger = logging.getLogger('run-on-ec2')


class SSH():
"""
Expand All @@ -15,15 +19,13 @@ class SSH():
On exit the connection is closed.
:param host: Host to connect to.
:type host: str
:param user: User to connect with.
:type user: str
:param private_key: RSA private key.
:type private: str
Arguments:
host: Host to connect to.
user: User to connect with.
private_key: RSA private key.
"""

def __init__(self, host, user, private_key):
def __init__(self, host: str, user: str, private_key: RSAKey):
self.host = host
self.user = user
self.private_key = RSAKey.from_private_key(StringIO(private_key))
Expand All @@ -34,12 +36,12 @@ def __enter__(self):
user=self.user,
connect_kwargs={'pkey': self.private_key},
)
print(f'Waiting for SSH to become available on {self.host}...')
logger.info(f'Waiting for SSH to become available on {self.host}...')
self.wait_for_ssh(default_timer())
return self.connection

def __exit__(self, type, value, traceback):
print(f'Closing SSH connection to {self.host}...')
logger.info(f'Closing SSH connection to {self.host}...')
self.connection.close()

def wait_for_ssh(self, start):
Expand Down
23 changes: 18 additions & 5 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,40 @@
from contextlib import ExitStack
import logging
import os
import sys
from uuid import uuid4

from lib.ec2 import TempKeyPair, TempInstance
from lib.ssh import SSH

if __name__ == '__main__':
logger = logging.getLogger('run-on-ec2')


def main():
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())

name = os.environ['NAME']
unique_name = f'{name}-{uuid4()}'
key_name = f'{name}-{uuid4()}'
launch_template_name = os.environ['LAUNCH_TEMPLATE_NAME']
subnet_id = os.environ['SUBNET_ID']
command = ' '.join(sys.argv[1:])

with ExitStack() as stack:
private_key = stack.enter_context(TempKeyPair(unique_name))
private_key = stack.enter_context(TempKeyPair(key_name))
instance = stack.enter_context(TempInstance(
unique_name,
name,
launch_template_name,
key_name,
subnet_id,
))
host = instance.private_ip_address
connection = stack.enter_context(SSH(host, 'ec2-user', private_key))

escaped_command = command.replace("'", "\\'")
print(f'Running command "{escaped_command}" on {host}...')
logger.info(f'Running command "{escaped_command}" on {host}...')
connection.run(f'exec $SHELL -l -c \'{escaped_command}\'')


if __name__ == '__main__':
main()

0 comments on commit 6bd2358

Please sign in to comment.