Skip to content
This repository has been archived by the owner on Nov 15, 2024. It is now read-only.

Commit

Permalink
Add upload-build and get-game-session-log
Browse files Browse the repository at this point in the history
  • Loading branch information
kyleknap committed Feb 8, 2016
1 parent 9c75270 commit 926fef4
Show file tree
Hide file tree
Showing 15 changed files with 673 additions and 9 deletions.
4 changes: 3 additions & 1 deletion awscli/customizations/argrename.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
'codepipeline.delete-custom-action-type.version': 'action-version',
'route53.delete-traffic-policy.version': 'traffic-policy-version',
'route53.get-traffic-policy.version': 'traffic-policy-version',
'route53.update-traffic-policy-comment.version': 'traffic-policy-version'
'route53.update-traffic-policy-comment.version': 'traffic-policy-version',
'gamelift.create-build.version': 'build-version',
'gamelift.update-build.version': 'build-version'
}

# Same format as ARGUMENT_RENAMES, but instead of renaming the arguments,
Expand Down
23 changes: 23 additions & 0 deletions awscli/customizations/gamelift/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from awscli.customizations.gamelift.uploadbuild import UploadBuildCommand
from awscli.customizations.gamelift.getlog import GetGameSessionLogCommand


def register_gamelift_commands(event_emitter):
event_emitter.register('building-command-table.gamelift', inject_commands)


def inject_commands(command_table, session, **kwargs):
command_table['upload-build'] = UploadBuildCommand(session)
command_table['get-game-session-log'] = GetGameSessionLogCommand(session)
58 changes: 58 additions & 0 deletions awscli/customizations/gamelift/getlog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import urllib2
import sys
from functools import partial

from awscli.customizations.commands import BasicCommand


class GetGameSessionLogCommand(BasicCommand):
NAME = 'get-game-session-log'
DESCRIPTION = 'Download a compressed log file for a game session.'
ARG_TABLE = [
{'name': 'game-session-id', 'required': True,
'help_text': 'The game session ID'},
{'name': 'save-as', 'required': True,
'help_text': 'The filename to which the file should be saved (.zip)'}
]

def _run_main(self, args, parsed_globals):
client = self._session.create_client(
'gamelift', region_name=parsed_globals.region,
endpoint_url=parsed_globals.endpoint_url,
verify=parsed_globals.verify_ssl
)

# Retrieve a signed url.
response = client.get_game_session_log_url(
GameSessionId=args.game_session_id)
url = response['PreSignedUrl']

# Retrieve the content from the presigned url and save it locally.
contents = urllib2.urlopen(url)

sys.stdout.write(
'Downloading log archive for game session %s...\r' %
args.game_session_id
)

with open(args.save_as, 'wb') as f:
for chunk in iter(partial(contents.read, 1024), b''):
f.write(chunk)

sys.stdout.write(
'Successfully downloaded log archive for game '
'session %s to %s\n' % (args.game_session_id, args.save_as))

return 0
128 changes: 128 additions & 0 deletions awscli/customizations/gamelift/uploadbuild.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import threading
import contextlib
import os
import tempfile
import sys
import zipfile

from s3transfer import S3Transfer

from awscli.customizations.commands import BasicCommand
from awscli.customizations.s3.utils import human_readable_size


class UploadBuildCommand(BasicCommand):
NAME = 'upload-build'
DESCRIPTION = 'Upload a new build to AWS GameLift.'
ARG_TABLE = [
{'name': 'name', 'required': True,
'help_text': 'The name of the build'},
{'name': 'build-version', 'required': True,
'help_text': 'The version of the build'},
{'name': 'build-root', 'required': True,
'help_text': 'The root directory of build to upload'}
]

def _run_main(self, args, parsed_globals):
gamelift_client = self._session.create_client(
'gamelift', region_name=parsed_globals.region,
endpoint_url=parsed_globals.endpoint_url,
verify=parsed_globals.verify_ssl
)
# Create a build.
response = gamelift_client.create_build(
Name=args.name, Version=args.build_version)
build_id = response['Build']['BuildId']

# Retrieve a set of credentials and the s3 bucket and key.
response = gamelift_client.request_upload_credentials(
BuildId=build_id)
upload_credentials = response['UploadCredentials']
bucket = response['StorageLocation']['Bucket']
key = response['StorageLocation']['Key']

# Create the S3 Client for uploading the build based on the
# credentials returned from creating the build.
access_key = upload_credentials['AccessKeyId']
secret_key = upload_credentials['SecretAccessKey']
session_token = upload_credentials['SessionToken']
s3_client = self._session.create_client(
's3', aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
aws_session_token=session_token,
region_name=parsed_globals.region,
verify=parsed_globals.verify_ssl
)

s3_transfer_mgr = S3Transfer(s3_client)

try:
fd, temporary_zipfile = tempfile.mkstemp('%s.zip' % build_id)
zip_directory(temporary_zipfile, args.build_root)
s3_transfer_mgr.upload_file(
temporary_zipfile, bucket, key,
callback=ProgressPercentage(
temporary_zipfile,
label='Uploading ' + args.build_root + ':'
)
)
finally:
os.close(fd)
os.remove(temporary_zipfile)

sys.stdout.write(
'Successfully uploaded %s to AWS GameLift\n'
'Build ID: %s\n' % (args.build_root, build_id))

return 0


def zip_directory(zipfile_name, source_root):
source_root = os.path.abspath(source_root)
with open(zipfile_name, 'wb') as f:
zip_file = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED, True)
with contextlib.closing(zip_file) as zf:
for root, dirs, files in os.walk(source_root):
for filename in files:
full_path = os.path.join(root, filename)
relative_path = os.path.relpath(
full_path, source_root)
zf.write(full_path, relative_path)


# TODO: Remove this class once available to CLI from s3transfer
# docstring.
class ProgressPercentage(object):
def __init__(self, filename, label=None):
self._filename = filename
self._label = label
if self._label is None:
self._label = self._filename
self._size = float(os.path.getsize(filename))
self._seen_so_far = 0
self._lock = threading.Lock()

def __call__(self, bytes_amount):
with self._lock:
self._seen_so_far += bytes_amount
if self._size > 0:
percentage = (self._seen_so_far / self._size) * 100
sys.stdout.write(
"\r%s %s / %s (%.2f%%)" % (
self._label, human_readable_size(self._seen_so_far),
human_readable_size(self._size), percentage
)
)
sys.stdout.flush()
2 changes: 2 additions & 0 deletions awscli/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
from awscli.customizations.iot_data import register_custom_endpoint_note
from awscli.customizations.iot import register_create_keys_and_cert_arguments
from awscli.customizations.iot import register_create_keys_from_csr_arguments
from awscli.customizations.gamelift import register_gamelift_commands


def awscli_initialize(event_handlers):
Expand Down Expand Up @@ -144,3 +145,4 @@ def awscli_initialize(event_handlers):
'building-argument-table.iot.create-certificate-from-csr',
register_create_keys_from_csr_arguments)
register_cloudfront(event_handlers)
register_gamelift_commands(event_handlers)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ docutils>=0.10
# in tandem, so we're requiring the latest develop
# branch of botocore when working on the awscli.
-e git://github.com/boto/botocore.git@develop#egg=botocore
-e git://github.com/boto/s3transfer.git@develop#egg=s3transfer
-e git://github.com/boto/jmespath.git@develop#egg=jmespath
nose==1.3.0
colorama>=0.2.5,<=0.3.3
Expand Down
11 changes: 6 additions & 5 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ universal = 1

[metadata]
requires-dist =
botocore==1.3.23
colorama>=0.2.5,<=0.3.3
docutils>=0.10
rsa>=3.1.2,<=3.3.0
argparse>=1.1; python_version=="2.6"
botocore==1.3.23
colorama>=0.2.5,<=0.3.3
docutils>=0.10
rsa>=3.1.2,<=3.3.0
s3transfer==0.0.1
argparse>=1.1; python_version=="2.6"
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
requires = ['botocore==1.3.23',
'colorama>=0.2.5,<=0.3.3',
'docutils>=0.10',
'rsa>=3.1.2,<=3.3.0']
'rsa>=3.1.2,<=3.3.0',
's3transfer==0.0.1']


if sys.version_info[:2] == (2, 6):
Expand Down
12 changes: 12 additions & 0 deletions tests/functional/gamelift/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
62 changes: 62 additions & 0 deletions tests/functional/gamelift/test_get_game_session_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os

from awscli.testutils import BaseAWSCommandParamsTest, FileCreator, mock
from awscli.compat import six


class TestGetGameSessionLog(BaseAWSCommandParamsTest):

prefix = 'gamelift get-game-session-log'

def setUp(self):
super(TestGetGameSessionLog, self).setUp()
self.files = FileCreator()
self.filename = os.path.join(self.files.rootdir, 'myfile')
self.urlopen_patch = mock.patch('urllib2.urlopen')
self.contents = b'My Contents'
self.urlopen_mock = self.urlopen_patch.start()
self.urlopen_mock.return_value = six.BytesIO(self.contents)

def tearDown(self):
super(TestGetGameSessionLog, self).tearDown()
self.files.remove_all()
self.urlopen_patch.stop()

def test_get_game_session_log(self):
cmdline = self.prefix
cmdline += ' --game-session-id mysession'
cmdline += ' --save-as %s' % self.filename

self.parsed_responses = [{'PreSignedUrl': 'myurl'}]
stdout, stderr, rc = self.run_cmd(cmdline, expected_rc=0)
self.assertEqual(len(self.operations_called), 1)
self.assertEqual(
self.operations_called[0][0].name, 'GetGameSessionLogUrl')
self.assertEqual(
self.operations_called[0][1],
{'GameSessionId': 'mysession'}
)

# Ensure the contents were saved to the file
self.assertTrue(os.path.exists(self.filename))
with open(self.filename, 'rb') as f:
self.assertEqual(f.read(), self.contents)

# Ensure the output is as expected
self.assertIn(
'Successfully downloaded log archive for game session '
'mysession to %s' % self.filename,
stdout
)
Loading

0 comments on commit 926fef4

Please sign in to comment.