Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/awscli/customizations/gamelift/uploadbuild.py
2635 views
1
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
import threading
14
import contextlib
15
import os
16
import tempfile
17
import sys
18
import zipfile
19
20
from s3transfer import S3Transfer
21
22
from awscli.customizations.commands import BasicCommand
23
from awscli.customizations.s3.utils import human_readable_size
24
from awscli.utils import create_nested_client
25
26
27
def parse_tags(raw_tags_list):
28
"""Parse tags from Key=Value format to GameLift API format."""
29
tags_list = []
30
if raw_tags_list:
31
for tag in raw_tags_list:
32
if '=' in tag:
33
key, value = tag.split('=', 1)
34
else:
35
key, value = tag, ''
36
tags_list.append({'Key': key, 'Value': value})
37
return tags_list
38
39
40
class UploadBuildCommand(BasicCommand):
41
NAME = 'upload-build'
42
DESCRIPTION = 'Upload a new build to AWS GameLift.'
43
ARG_TABLE = [
44
{'name': 'name', 'required': True,
45
'help_text': 'The name of the build'},
46
{'name': 'build-version', 'required': True,
47
'help_text': 'The version of the build'},
48
{'name': 'build-root', 'required': True,
49
'help_text':
50
'The path to the directory containing the build to upload'},
51
{'name': 'server-sdk-version', 'required': False,
52
'help_text':
53
'The version of the GameLift server SDK used to '
54
'create the game server'},
55
{'name': 'operating-system', 'required': False,
56
'help_text': 'The operating system the build runs on'},
57
{'name': 'tags', 'required': False, 'nargs': '+',
58
'help_text': 'Tags to assign to the build. Format: Key=Value'}
59
]
60
61
def _run_main(self, args, parsed_globals):
62
gamelift_client = create_nested_client(
63
self._session, 'gamelift', region_name=parsed_globals.region,
64
endpoint_url=parsed_globals.endpoint_url,
65
verify=parsed_globals.verify_ssl
66
)
67
# Validate a build directory
68
if not validate_directory(args.build_root):
69
sys.stderr.write(
70
f'Fail to upload {args.build_root}. '
71
'The build root directory is empty or does not exist.\n'
72
)
73
74
return 255
75
# Create a build based on the operating system given.
76
create_build_kwargs = {
77
'Name': args.name,
78
'Version': args.build_version
79
}
80
if args.operating_system:
81
create_build_kwargs['OperatingSystem'] = args.operating_system
82
if args.server_sdk_version:
83
create_build_kwargs['ServerSdkVersion'] = args.server_sdk_version
84
if args.tags:
85
create_build_kwargs['Tags'] = parse_tags(args.tags)
86
response = gamelift_client.create_build(**create_build_kwargs)
87
build_id = response['Build']['BuildId']
88
89
# Retrieve a set of credentials and the s3 bucket and key.
90
response = gamelift_client.request_upload_credentials(
91
BuildId=build_id)
92
upload_credentials = response['UploadCredentials']
93
bucket = response['StorageLocation']['Bucket']
94
key = response['StorageLocation']['Key']
95
96
# Create the S3 Client for uploading the build based on the
97
# credentials returned from creating the build.
98
access_key = upload_credentials['AccessKeyId']
99
secret_key = upload_credentials['SecretAccessKey']
100
session_token = upload_credentials['SessionToken']
101
s3_client = create_nested_client(
102
self._session, 's3',
103
aws_access_key_id=access_key,
104
aws_secret_access_key=secret_key,
105
aws_session_token=session_token,
106
region_name=parsed_globals.region,
107
verify=parsed_globals.verify_ssl
108
)
109
110
s3_transfer_mgr = S3Transfer(s3_client)
111
112
try:
113
fd, temporary_zipfile = tempfile.mkstemp(f'{build_id}.zip')
114
zip_directory(temporary_zipfile, args.build_root)
115
s3_transfer_mgr.upload_file(
116
temporary_zipfile, bucket, key,
117
callback=ProgressPercentage(
118
temporary_zipfile,
119
label='Uploading ' + args.build_root + ':'
120
)
121
)
122
finally:
123
os.close(fd)
124
os.remove(temporary_zipfile)
125
126
sys.stdout.write(
127
f'Successfully uploaded {args.build_root} to AWS GameLift\n'
128
f'Build ID: {build_id}\n')
129
130
return 0
131
132
133
def zip_directory(zipfile_name, source_root):
134
source_root = os.path.abspath(source_root)
135
with open(zipfile_name, 'wb') as f:
136
zip_file = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED, True)
137
with contextlib.closing(zip_file) as zf:
138
for root, dirs, files in os.walk(source_root):
139
for filename in files:
140
full_path = os.path.join(root, filename)
141
relative_path = os.path.relpath(
142
full_path, source_root)
143
zf.write(full_path, relative_path)
144
145
146
def validate_directory(source_root):
147
# For Python26 on Windows, passing an empty string equates to the
148
# current directory, which is not intended behavior.
149
if not source_root:
150
return False
151
# We walk the root because we want to validate there's at least one file
152
# that exists recursively from the root directory
153
for path, dirs, files in os.walk(source_root):
154
if files:
155
return True
156
return False
157
158
159
# TODO: Remove this class once available to CLI from s3transfer
160
# docstring.
161
class ProgressPercentage:
162
def __init__(self, filename, label=None):
163
self._filename = filename
164
self._label = label
165
if self._label is None:
166
self._label = self._filename
167
self._size = float(os.path.getsize(filename))
168
self._seen_so_far = 0
169
self._lock = threading.Lock()
170
171
def __call__(self, bytes_amount):
172
with self._lock:
173
self._seen_so_far += bytes_amount
174
if self._size > 0:
175
percentage = (self._seen_so_far / self._size) * 100
176
sys.stdout.write(
177
f"\r{self._label} {human_readable_size(self._seen_so_far)} / {human_readable_size(self._size)} ({percentage:.2f}%)"
178
)
179
sys.stdout.flush()
180
181