gym_recording/storage_s3.py (38 lines of code) (raw):
import math, random, time, logging, re, base64, argparse, collections, sys, os, threading
import numpy as np
import boto3
import gym
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
__all__ = ['upload_recording', 'download_recording']
cookie_rng = random.SystemRandom() # initialized from /dev/urandom
def random_token(l=3):
return ''.join([cookie_rng.choice('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz') for i in range(l)])
def upload_recording(directory, env_name, bucket):
"""
Upload the recording saved in directory to bucket. Returns an s3://... URL
"""
s3 = boto3.resource('s3')
token = random_token(3)
s3dir = os.path.join('recording', env_name, time.strftime('%Y-%m-%d_%H-%M-%S') + '_' + token)
logger.info('Scanning %s', directory)
b = s3.Bucket(bucket)
for fn in os.listdir(directory):
logger.info('Upload %s to %s', fn, os.path.join(s3dir, fn))
b.upload_file(Filename=os.path.join(directory, fn), Key=os.path.join(s3dir, fn))
return 's3://' + bucket + '/' + s3dir
def download_recording(s3url):
"""
Download the recording saved in s3url to a directory in /tmp. It'll reuse the cached
recording if it's already downloaded. Returns a directory
"""
s3 = boto3.resource('s3')
m = re.match(r's3://([\w\-]+)/([\w\-\.\/]+)', s3url)
if not m:
raise Exception('Invalid s3 URL')
bucket = m.group(1)
bucketdir = m.group(2)
directory = os.path.join('/tmp', bucketdir)
if not os.access(directory, os.R_OK):
directory_tmp = directory + '.tmp{}'.format(os.getpid())
os.makedirs(directory_tmp)
b = s3.Bucket(bucket)
for fn in b.objects.filter(Prefix=bucketdir+'/'):
logger.info('Download %s to %s', fn.key, directory)
localfn = os.path.join(directory_tmp, os.path.basename(fn.key))
b.download_file(Key=fn.key, Filename=localfn)
os.rename(directory_tmp, directory)
return directory