Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-introduce create-dock & destroy-dock that use AWS servicecatalog API #61

Merged
merged 3 commits into from
Sep 23, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The commands used to operate against these entities are:

| CommandSet / Entity | Creation | Execution | Notebook | Utility |
|----------------------|:------------------------:|:--------------------:|:------------:|:----------------------------:|
| Dock | register-dock <br/> unregister-dock | start-dock <br/> stop-dock | nb-dock | source dock <br/> ls-dock <br/> ssh-dock |
| Dock | create-dock <br/> destroy-dock | start-dock <br/> stop-dock | nb-dock | source dock <br/> ls-dock <br/> ssh-dock |
| Image | build-image | run-image | run-notebook | publish-image <br/> transfer-image |

Possible use cases include:
Expand Down Expand Up @@ -87,9 +87,9 @@ A "dock" is a remote system that you can connect to through `ssh`. You can "dock
any docker commands, including image and notebook cli above will be run against the remote docker server. Once a "dock"
is created, you can dock your terminal by issuing the command `source dock <server IP or moniker>`

`register-dock` is used to add a remote system to the dock list with all its configuration (username, ip and a moniker)
`create-dock` (`register-dock` if provisioning from AWS console) is used to add a remote system to the dock list with all its configuration (username, ip and a moniker)

`unregister-dock` is used to remove the reference to the remote system
`destroy-dock` (`unregister-dock` if provisioned from AWS console) is used to remove the reference to the remote system

`stop-dock` will change the instances state of a remote dock to `stopped`

Expand Down
131 changes: 128 additions & 3 deletions scripts/create-dock
Original file line number Diff line number Diff line change
@@ -1,4 +1,129 @@
#!/usr/bin/env bash
set -e
#!/usr/bin/env python3

echo "create-dock has been deprecated, please use AWS Service Catalog"
from __future__ import print_function
import os
import boto3
import argparse
import getpass
import time
import sys

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("dock", help="Name of dock to create")
parser.add_argument("-i", "--instance_type", help="instance type to launch", default=None)
parser.add_argument("-s", "--subnet", help="subnet ID for instance", default=None)
parser.add_argument("-v", "--volume_size", help="Size of block device (in GBytes)", type=int, default=None)
args = parser.parse_args()

provisioning_artifact_id = None
path_id = None
ip = None
instance_id = None

# first, we need to find the Basic EC2 Instance product
client = boto3.client('servicecatalog')
response = client.search_products(Filters={
'FullTextSearch': ['Basic EC2 instance']
rappdw marked this conversation as resolved.
Show resolved Hide resolved
})
product_id = response['ProductViewSummaries'][0]['ProductId']

# now we need to find the latest version of this product
response = client.describe_product(Id=product_id)
for provisioning_artifact in response['ProvisioningArtifacts']:
if provisioning_artifact['Name'] == 'latest':
provisioning_artifact_id = provisioning_artifact['Id']

# currently, our products don't have a default path, it would be nice if they were configured
# with default path = "launch", but for now, find the launch path
response = client.list_launch_paths(ProductId=product_id)
for launch_path in response['LaunchPathSummaries']:
for constraint in launch_path['ConstraintSummaries']:
if constraint['Type'] == 'LAUNCH':
path_id = launch_path['Id']

# get the provisioning parameters and plug in any overrides
response = client.describe_provisioning_parameters(ProductId=product_id,
ProvisioningArtifactId=provisioning_artifact_id,
PathId=path_id)

parameters = []
for parameter in response['ProvisioningArtifactParameters']:
key = parameter['ParameterKey']
value = parameter['DefaultValue']
allowed_values = parameter['ParameterConstraints']['AllowedValues']
value_override = None
if key == 'InstanceName':
value_override = args.dock
elif key == 'InstanceType':
value_override = args.instance_type
elif key == 'SubnetID':
value_override = args.subnet
elif key == 'VolumeSize':
value_override = args.volume_size
if value_override and (not allowed_values or value_override in allowed_values):
parameters.append({'Key': key, 'Value':value_override})

# now provision product
if (product_id and provisioning_artifact_id and path_id):
response = client.provision_product(ProductId=product_id,
PathId=path_id,
ProvisioningArtifactId=provisioning_artifact_id,
ProvisionedProductName=args.dock,
ProvisioningParameters=parameters,
Tags=[
{
'Key': 'Name',
'Value': args.dock
},
{
'Key': 'business_unit',
'Value': 'Compliance and Digital Risk'
},
{
'Key': 'component',
'Value': 'ec2 instance'
},
{
'Key': 'product',
'Value': 'ML Labs'
},
{
'Key': 'support_level',
'Value': 'dev'
},
{
'Key': 'created_by',
'Value': getpass.getuser()
}
]
)
record_id = response['RecordDetail']['RecordId']
provisioned_product_id = response['RecordDetail']['ProvisionedProductId']
sys.stdout.write('waiting for launch to complete')
while ip is None:
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(2)
response = client.describe_record(Id=record_id)
for output in response['RecordOutputs']:
if output['OutputKey'] == 'InstanceID':
instance_id = output['OutputValue']
elif output['OutputKey'] == 'PrivateIP':
ip = output['OutputValue']
sys.stdout.write('\n')
sys.stdout.flush()

if 'DOCK_USER' in os.environ:
user = os.environ['DOCK_USER']
else:
user = 'ubuntu'
cfg_dir = os.path.join(os.path.join(os.path.expanduser("~"), ".docker"), ip)
os.makedirs(cfg_dir, exist_ok=True)
f = open(os.path.join(cfg_dir, 'connection_config.txt'), 'w')
f.write("DOCK_USER={user}\n".format(user=user))
f.write("DOCK_MONIKER={moniker}\n".format(moniker=args.dock))
f.write("DOCK_HOSTNAME={ip}\n".format(ip=ip))
f.write("DOCK_IP={ip}\n".format(ip=ip))
f.write("DOCK_PROVISIONED_PRODUCT={id}\n".format(id=provisioned_product_id))
f.close()
59 changes: 56 additions & 3 deletions scripts/destroy-dock
Original file line number Diff line number Diff line change
@@ -1,4 +1,57 @@
#!/usr/bin/env bash
set -e
#!/usr/bin/env python3
from __future__ import print_function
import os
import boto3
import argparse
import fnmatch
import sys
import time
import shutil

echo "delete-dock has been deprecated, please use AWS Service Catalog"
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("dock", help="Name of dock to destroy")
args = parser.parse_args()

ip = None
provisioned_product_id = None

cfg_dir = os.path.join(os.path.expanduser("~"), ".docker")
for root, dirs, files in os.walk(cfg_dir):
for file in files:
if fnmatch.fnmatch(file, 'connection_config.txt'):
vars = {}
with open(os.path.join(root, file)) as f:
for line in f:
if line.startswith('#'):
continue
key, value = line.strip().split('=', 1)
vars[key] = value
if vars['DOCK_MONIKER'] == args.dock or vars['DOCK_IP'] == args.dock:
ip = vars['DOCK_IP']
provisioned_product_id = vars['DOCK_PROVISIONED_PRODUCT']

client = boto3.client('servicecatalog')
response = client.terminate_provisioned_product(ProvisionedProductId=provisioned_product_id)

record_id = response['RecordDetail']['RecordId']
errors = None
sys.stdout.write('waiting for terminate to complete')
while True:
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(2)
response = client.describe_record(Id=record_id)
if response['RecordDetail']['Status'] == 'SUCCEEDED':
shutil.rmtree(os.path.join(cfg_dir, ip), ignore_errors=True)
break
elif response['RecordDetail']['Status'] in ['IN_PROGRESS_IN_ERROR', 'FAILED']:
sys.stdout.write('\n')
sys.stdout.flush()
errors = response['RecordDetail']['RecordErrors']
break
sys.stdout.write('\n')
sys.stdout.flush()

if errors:
print(errors)
2 changes: 0 additions & 2 deletions scripts/register-dock
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ echo "DOCK_USER:$DOCK_USER"
echo "MONIKER:$MONIKER"
echo "HOSTNAME:$IP"

### copy CA to client.
mkdir -p ~/.docker/${IP}

DOCKER_CFG_FILE=$(python3 -c "import pkg_resources; print(pkg_resources.resource_filename('dockerutils', 'docker-server-daemon.json'))")
printf "DOCK_USER=$DOCK_USER\nDOCK_MONIKER=$MONIKER\nDOCK_HOSTNAME=$IP\nDOCK_IP=$IP\n" > $HOME/.docker/${IP}/connection_config.txt