Integrating github/staging through commit ab87ed9
This commit is contained in:
@@ -1,745 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Requires boto3 >= 1.12.4
|
||||
"""
|
||||
|
||||
import configparser
|
||||
import shutil
|
||||
import os
|
||||
import logging
|
||||
import pytest
|
||||
import six
|
||||
|
||||
pytest.importorskip("botocore")
|
||||
from botocore.exceptions import ClientError
|
||||
pytest.importorskip("boto3")
|
||||
import boto3
|
||||
|
||||
import ly_test_tools.environment.process_utils as process_utils
|
||||
import ly_test_tools.environment.file_system as file_system
|
||||
from . import s3_utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
DEFAULT_REGION = 'us-west-2'
|
||||
|
||||
|
||||
class GameliftUtils(object):
|
||||
"""
|
||||
Stores a boto3 gamelift client to use for AWS Gamelift functionalities. Static methods are provided when no client
|
||||
is needed for the function.
|
||||
"""
|
||||
def __init__(self, session=None):
|
||||
# type: (boto3.Session) -> None
|
||||
"""
|
||||
The gamelift client can be set during init, or a default one will be created.
|
||||
:param client: A boto3 gamelift client
|
||||
"""
|
||||
self._client = None
|
||||
self._session = session
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
if self._client is None:
|
||||
if self._session:
|
||||
self._client = self._session.client('gamelift')
|
||||
else:
|
||||
logger.info(f"No client provided, using default credentials with default region: {DEFAULT_REGION}")
|
||||
self._client = boto3.Session(region_name=DEFAULT_REGION).client('gamelift')
|
||||
return self._client
|
||||
|
||||
@staticmethod
|
||||
def get_aws_credentials(profile):
|
||||
# type: () -> dict
|
||||
"""
|
||||
Gets credentials from .aws/credentials file. QA will setup our nodes using one default set for Isengard
|
||||
:profile: The profile name found in the credentials file. ex. [default]
|
||||
:return: Dictionary of AWS Credentials gathered
|
||||
"""
|
||||
aws_credentials = {}
|
||||
home = os.path.expanduser("~")
|
||||
IniRead = configparser.ConfigParser()
|
||||
IniRead.read(os.path.join(home, '.aws', 'credentials'))
|
||||
aws_credentials['access'] = IniRead.get(profile, 'aws_access_key_id')
|
||||
aws_credentials['secret'] = IniRead.get(profile, 'aws_secret_access_key')
|
||||
return aws_credentials
|
||||
|
||||
# This method is static because it uses the AWS CLI to upload a build locally. Boto3 only provides a way to create
|
||||
# a gamelift build from s3.
|
||||
@staticmethod
|
||||
def upload_build_to_gamelift(source_folder, name, operating_system, region, build_version='1.0.0'):
|
||||
# type: (str, str, str, str, str) -> str
|
||||
"""
|
||||
Function to upload a build to gamelift. We use AWS CLI because boto3 can only create builds from s3
|
||||
|
||||
:param source_folder: Path to the build zip file
|
||||
:param name: Name of the Gamelift Build
|
||||
:param operating_system: Operating system parameter for the aws gamelift upload-build command
|
||||
:param region: AWS region, such as us-east-1
|
||||
:param build_version: Build version as a string
|
||||
Example command:
|
||||
aws gamelift upload-build --name "1.11 pc-47 (profile)" E:/lyengine/dev --build-version 1.11 --build-root
|
||||
"F:\\lumberyard-1.11-472772-pc-47\\dev\\multiplayersample_pc_paks_dedicated" --operating-system WINDOWS_2012
|
||||
--region us-west-2
|
||||
"""
|
||||
|
||||
upload_build_cmd = ['aws', 'gamelift', 'upload-build',
|
||||
'--build-root', source_folder,
|
||||
'--name', name,
|
||||
'--operating-system', operating_system,
|
||||
'--region', region,
|
||||
'--build-version', build_version,
|
||||
]
|
||||
|
||||
# We have to use AWS CLI to upload build from local machine, boto3 only uploads builds from S3
|
||||
# shell=True only works on Windows.
|
||||
output = process_utils.check_output(upload_build_cmd, shell=True)
|
||||
|
||||
# Grab the line containing the Build ID
|
||||
build_output_line = ''
|
||||
for output_line in output.splitlines():
|
||||
if "Build ID" in output_line:
|
||||
build_output_line = output_line
|
||||
break
|
||||
|
||||
# Check to see if the build was uploaded successfully
|
||||
assert build_output_line, f"Gamelift build was not successfully uploaded: {output}"
|
||||
|
||||
# Find the build id and strip it for escape characters
|
||||
build_id = build_output_line[build_output_line.index('build-'):].rstrip()
|
||||
|
||||
logger.info(f"Build: {build_id} was successfully uploaded.")
|
||||
return build_id
|
||||
|
||||
def create_gamelift_queue(self, queue_name, destination_fleet_arn, timeout=120):
|
||||
"""
|
||||
Sends request to create a gamelift queue.
|
||||
:param queue_name: Name of the queue to create
|
||||
:param destination_fleet_arn: GameLift fleet the queue will place game sessions on.
|
||||
:param timeout: Placement request timeout
|
||||
:return: Queue Arn of queue created
|
||||
"""
|
||||
try:
|
||||
response = self.client.create_game_session_queue(
|
||||
Name=queue_name,
|
||||
TimeoutInSeconds=timeout,
|
||||
Destinations=[
|
||||
{
|
||||
'DestinationArn': destination_fleet_arn
|
||||
},
|
||||
]
|
||||
)
|
||||
created_game_session_queue = response['GameSessionQueue']
|
||||
queue_arn = created_game_session_queue['GameSessionQueueArn']
|
||||
except Exception as e:
|
||||
raise AssertionError('GameLift queue:{} creation failed for fleetArn:{} Error:{}'.format(queue_name,
|
||||
destination_fleet_arn,
|
||||
e))
|
||||
logger.info('GameLift queue:{} created Arn:{}'.format(queue_name, queue_arn))
|
||||
return queue_arn
|
||||
|
||||
def create_gamelift_matchmaking_rule_set(self, rule_set_name):
|
||||
"""
|
||||
Creates GameLift matchmaking rule set
|
||||
:param rule_set_name: Name of the rule set
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
rule_set = '{"name": "player_vs_asteriods", "ruleLanguageVersion": "1.0", "teams": [{"name": "Players",' \
|
||||
'"maxPlayers": 4,"minPlayers": 2}]} '
|
||||
self.client.create_matchmaking_rule_set(
|
||||
Name=rule_set_name,
|
||||
RuleSetBody=rule_set
|
||||
)
|
||||
except ClientError as e:
|
||||
raise AssertionError('GameLift matchmaking rule set:{} creation failed. Error {}'.format(rule_set_name, e))
|
||||
logger.info('GameLift matchmaking rule set:{}'.format(rule_set_name))
|
||||
|
||||
def create_gamelift_matchmaking_config(self, config_name, queue_arn, rules_set_name, backfill_mode):
|
||||
"""
|
||||
Creates GameLift matchmaking config
|
||||
:param config_name: Name of the matchmaking config
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
response = self.client.create_matchmaking_configuration(
|
||||
Name=config_name,
|
||||
Description='MS Test config',
|
||||
GameSessionQueueArns=[
|
||||
queue_arn
|
||||
],
|
||||
RequestTimeoutSeconds=120,
|
||||
AcceptanceRequired=False,
|
||||
RuleSetName=rules_set_name,
|
||||
GameProperties=[
|
||||
{
|
||||
'Key': 'sv_name',
|
||||
'Value': 'MSTestServer'
|
||||
},
|
||||
{
|
||||
'Key': 'sv_map',
|
||||
'Value': 'multiplayersample'
|
||||
}
|
||||
],
|
||||
BackfillMode= backfill_mode
|
||||
)
|
||||
except ClientError as e:
|
||||
raise AssertionError('GameLift matchmaking config :{} creation failed {}'.format(config_name, e))
|
||||
logger.info('GameLift matchmaking config:{}'.format(config_name))
|
||||
|
||||
def create_gamelift_fleet(self, name, build_id, ec2_instance_type, runtime_configuration,
|
||||
ec2_inbound_permissions):
|
||||
"""
|
||||
Function to create a fleet within Gamelift
|
||||
:param name: Name of the Gamelift Fleet
|
||||
:param build_id: The id of the build given back to us from Gamelift when we uploaded a build
|
||||
:param ec2_instance_type: AWS instance type, such as 'c3.large'
|
||||
:param runtime_configuration: A dictionary of LaunchPath, Parameters, and ConcurrentExecutions
|
||||
:param ec2_inbound_permissions: A list of port settings for the fleet
|
||||
:return: ID,ARN of the fleet that was created
|
||||
"""
|
||||
for port_settings in ec2_inbound_permissions:
|
||||
if port_settings['IpRange'] == '0.0.0.0/0':
|
||||
logger.warning('WARNING - Creating fleet with IpRange 0.0.0.0/0 is a security risk. This setting can be'
|
||||
'changed in the networking_constants.py file')
|
||||
|
||||
output = self.client.create_fleet(Name=name,
|
||||
Description='Automated Test',
|
||||
BuildId=build_id,
|
||||
EC2InstanceType=ec2_instance_type,
|
||||
EC2InboundPermissions=ec2_inbound_permissions,
|
||||
RuntimeConfiguration=runtime_configuration
|
||||
)
|
||||
logger.info(output)
|
||||
try:
|
||||
fleet_id = output['FleetAttributes']['FleetId']
|
||||
fleet_arn = output['FleetAttributes']['FleetArn']
|
||||
return fleet_id, fleet_arn
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Error when trying to return Fleet Id from output: {output}.')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
def is_build_status_ready(self, build_id):
|
||||
# type: (str) -> bool
|
||||
"""
|
||||
Returns True if the build status is 'READY', otherwise returns False
|
||||
:param build_id: The AWS build-id to be checked
|
||||
:return: bool
|
||||
"""
|
||||
build_status = self.get_build_status(build_id)
|
||||
return build_status == 'READY'
|
||||
|
||||
def get_build_status(self, build_id):
|
||||
# type: (str) -> str
|
||||
"""
|
||||
Returns the status of the build. Possible statuses include the following: 'INITIALIZED', 'READY', 'FAILED'
|
||||
:param build_id: The AWS build-id to be checked
|
||||
:return: The status of the build as a string
|
||||
"""
|
||||
output = self.client.describe_build(BuildId=build_id)
|
||||
logger.debug(output)
|
||||
|
||||
try:
|
||||
build_status = output['Build']['Status']
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Cannot find Build Status in output: {output}.')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
return build_status
|
||||
|
||||
def check_instance_state(self, fleet_id):
|
||||
# type: (str) -> bool
|
||||
"""
|
||||
Checked the state of an instance. Returns true when the instance is in an Active state. Asserts when the fleet
|
||||
has an ERROR state, otherwise false
|
||||
:param fleet_id: the Id of the fleet we are checking the state of
|
||||
:return: True when fleet is in the active state
|
||||
"""
|
||||
fleet_attribute = self.get_fleet_list_attributes([fleet_id])
|
||||
|
||||
# We expect fleet_attribute to be a list of one element
|
||||
try:
|
||||
if len(fleet_attribute) == 0:
|
||||
logger.debug(f'Fleet attribute was empty for fleet: {fleet_id}')
|
||||
return False
|
||||
fleet_status = fleet_attribute[0]['Status']
|
||||
if fleet_status == 'ACTIVE':
|
||||
return True
|
||||
elif fleet_status == 'ERROR':
|
||||
raise AssertionError(f'Fleet: {fleet_id} has status ERROR!')
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Error occurred when checking fleet status for fleet: {fleet_id} from attribute: '
|
||||
f'{fleet_attribute}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
logger.debug(f'Fleet attribute was expected to be ACTIVE, but was: {fleet_status} for fleet {fleet_id}')
|
||||
return False
|
||||
|
||||
def get_fleet_list_attributes(self, fleet_ids=None):
|
||||
# type: (list[str]) -> list[str]
|
||||
"""
|
||||
Function to retrieve the fleet attribute for a given fleet. Will return all fleet attributes if no fleet_id is
|
||||
given
|
||||
:param fleet_ids: a list of fleet ids (can be a list of one) to get attributes for
|
||||
:return: list
|
||||
"""
|
||||
fleet_attribute_list = []
|
||||
|
||||
# Get paginator
|
||||
try:
|
||||
fleet_attribute_paginator = self.client.get_paginator('describe_fleet_attributes')
|
||||
except ClientError as e:
|
||||
problem = ClientError('Error occurred when getting paginator describe_fleet_attributes. Check to see if the'
|
||||
'botocore version is compatible')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Get iterator
|
||||
try:
|
||||
if fleet_ids:
|
||||
fleet_attribute_iterator = fleet_attribute_paginator.paginate(FleetIds=fleet_ids)
|
||||
else:
|
||||
fleet_attribute_iterator = fleet_attribute_paginator.paginate()
|
||||
except ClientError as e:
|
||||
problem = ClientError(f'Error occurred when getting fleet attributes for fleet: {fleet_ids}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Iterate through the fleet responses
|
||||
try:
|
||||
for fleet_response in fleet_attribute_iterator:
|
||||
for fleet_attribute in fleet_response['FleetAttributes']:
|
||||
fleet_attribute_list.append(fleet_attribute)
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Cannot find FleetAttributes in output: {fleet_response}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
return fleet_attribute_list
|
||||
|
||||
def check_all_current_player_sessions(self, fleet_id, number_of_players):
|
||||
# type: (str, int) -> bool
|
||||
"""
|
||||
Checks for the number of players inside a game session against the expected value.
|
||||
:param fleet_id: The ID of the gamelift fleet
|
||||
:param number_of_players: Number of players to be expecting for this function to verify
|
||||
:return: Returns true of the number of players match the number of connected clients
|
||||
"""
|
||||
game_sessions = self.get_game_sessions(fleet_id)
|
||||
if len(game_sessions) == 0:
|
||||
return False
|
||||
for game_session in game_sessions:
|
||||
if game_session['CurrentPlayerSessionCount'] != number_of_players:
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_total_players_in_all_game_sessions(self, fleet_id, number_of_players):
|
||||
"""
|
||||
Checks the number of players passed in is same as the total players in all game sessions on a Gamelift fleet.
|
||||
The reason to perform a check for total players instead of checking individual game sessions is the current
|
||||
MultiplayerSample implementation connects to the first game session it finds. In addition to that the response
|
||||
from GameLift describing active game sessions in a fleet is non-deterministic (order). So the best check is to
|
||||
verify total number of player sessions count
|
||||
:param fleet_id:
|
||||
:param number_of_players:
|
||||
:return:
|
||||
"""
|
||||
game_sessions = self.get_game_sessions(fleet_id)
|
||||
total_players = 0
|
||||
for game_session in game_sessions:
|
||||
total_players += game_session['CurrentPlayerSessionCount']
|
||||
|
||||
return number_of_players == total_players
|
||||
|
||||
def get_game_sessions(self, fleet_id=None):
|
||||
# type: (str) -> dict
|
||||
"""
|
||||
Gets a gamelift game session from the given fleet. Returns None if there is no active game_session for the fleet.
|
||||
If no fleet is provided, will return all game sessions.
|
||||
:param fleet_id: The ID of the gamelift fleet
|
||||
:return: The gamelift game session response
|
||||
"""
|
||||
game_sessions_list = []
|
||||
if fleet_id:
|
||||
logger.debug(f"Getting game session for fleet: {fleet_id}")
|
||||
|
||||
# Get paginator
|
||||
try:
|
||||
game_sessions_paginator = self.client.get_paginator('describe_game_sessions')
|
||||
except ClientError as e:
|
||||
problem = ClientError('Error occurred when getting paginator describe_game_sessions. Check to see if the '
|
||||
'botocore version is compatible')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Get iterator
|
||||
try:
|
||||
if fleet_id:
|
||||
game_sessions_iterator = game_sessions_paginator.paginate(FleetId=fleet_id)
|
||||
else:
|
||||
game_sessions_iterator = game_sessions_paginator.paginate()
|
||||
except ClientError as e:
|
||||
problem = ClientError('Error occurred when getting game sessions')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Iterate through the fleet responses
|
||||
try:
|
||||
for sessions_response in game_sessions_iterator:
|
||||
for game_session in sessions_response['GameSessions']:
|
||||
game_sessions_list.append(game_session)
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Cannot find GameSessions in output: {sessions_response}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
return game_sessions_list
|
||||
|
||||
@staticmethod
|
||||
def run_multiplayer_sample_paks_pc_dedicated(dev_path):
|
||||
# type: (str) -> None
|
||||
"""
|
||||
Runs a bat script which will package up the items needed for Gamelift
|
||||
:param dev_path: Path to the dev folder of Lumberyard
|
||||
"""
|
||||
run_paks_cmd = [dev_path + r'\BuildMultiplayerSample_Paks_PC_dedicated.bat']
|
||||
logger.info(process_utils.check_output(run_paks_cmd))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def run_multiplayer_sample_linux_packer(dev_path):
|
||||
# type: (str) -> str
|
||||
"""
|
||||
Runs a bat script which will create a .tar Gamelift package that can be transferred to a Linux machine
|
||||
:param dev_path: Path to the dev folder of Lumberyard
|
||||
:return: The full path of the tar package
|
||||
"""
|
||||
run_packer_cmd = os.path.join(dev_path, 'MultiplayerSample_LinuxPacker.bat')
|
||||
output = process_utils.check_output(run_packer_cmd)
|
||||
validation_text = 'Uncompressed archive successfully generated at '
|
||||
|
||||
for line in output.decode().split('\r'):
|
||||
if validation_text in line:
|
||||
tar_filepath = line[line.index(validation_text)+len(validation_text):].rstrip()
|
||||
return tar_filepath
|
||||
logger.info(output)
|
||||
raise AssertionError('tar package not found!')
|
||||
|
||||
@staticmethod
|
||||
def copy_bin_folder(bin_directory, dev_path):
|
||||
# type: (str, str) -> None
|
||||
"""
|
||||
Copies bin folder into the MultiplayerSamples_pc_Paks_Dedicated folder for Windows Gamelift Builds
|
||||
:param bin_directory: The bin directory to copy (usually the Dedicated Bin folder)
|
||||
:param dev_path: The path to the ~/dev directory
|
||||
:return: None
|
||||
"""
|
||||
to_folder = dev_path + '\\MultiplayerSample_pc_Paks_Dedicated\\' + bin_directory
|
||||
if os.path.exists(to_folder):
|
||||
file_system.delete([to_folder], True, True)
|
||||
shutil.copytree(os.path.join(dev_path, bin_directory), to_folder)
|
||||
|
||||
def download_required_gamelift_files(self, destination_dir):
|
||||
# type: (str) -> None
|
||||
"""
|
||||
Downloads required vc redistributables, their installation script, and debug libs from S3. Not all these files are
|
||||
required by Gamelift, but is for organizing our builds.
|
||||
:param destination_dir: Path on disk to be checked.
|
||||
"""
|
||||
s3_util = s3_utils.S3Utils(self._session)
|
||||
gamelift_files_bucket = 'ly-net-gamelift-required-files'
|
||||
keys = ['vc_redist.x64.exe', 'VC_redist2017.x64.exe', 'vcredist_x64.exe', 'install.bat',
|
||||
'msvcp140d.dll', 'ucrtbased.dll', 'vcruntime140d.dll']
|
||||
|
||||
for file_key in keys:
|
||||
s3_util.download_from_bucket(gamelift_files_bucket, file_key, destination_dir)
|
||||
|
||||
def create_gamelift_package(self, dev_path, bin_directory):
|
||||
# type: (str, str) -> None
|
||||
"""
|
||||
Creates a package ready to be deployed to gamelift via aws cli
|
||||
:param dev_path: Path to dev directory
|
||||
:param bin_directory: path to bin directory
|
||||
:return: None
|
||||
"""
|
||||
paks_dir = os.path.join(dev_path, 'MultiplayerSample_pc_Paks_Dedicated')
|
||||
|
||||
if os.path.exists(paks_dir):
|
||||
file_system.delete([paks_dir], True, True)
|
||||
|
||||
GameliftUtils.run_multiplayer_sample_paks_pc_dedicated(dev_path)
|
||||
self.download_required_gamelift_files(paks_dir)
|
||||
GameliftUtils.copy_bin_folder(f"{bin_directory}.Dedicated", dev_path)
|
||||
|
||||
def get_build_id_from_fleet_id(self, fleet_id):
|
||||
# type: (str) -> str
|
||||
"""
|
||||
Retreives the build id that a fleet was created from.
|
||||
:param fleet_id: The ID from a gamelift fleet
|
||||
:return: A gamelift build id
|
||||
"""
|
||||
fleet_attributes = self.get_fleet_list_attributes([fleet_id])
|
||||
# We expect a list of one element
|
||||
try:
|
||||
build_id = fleet_attributes[0]['BuildId']
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Cannot find build id in fleet attributes: {fleet_attributes} for fleet: {fleet_id}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
return build_id
|
||||
|
||||
def get_in_use_build_list_from_gamelift(self):
|
||||
# type: () -> list[str]
|
||||
"""
|
||||
Function that returns a list of in use builds. 'In use builds' include those that are being initalized and if an
|
||||
'in use fleet' is being created from that build. This function is used to target stale builds that are either idle
|
||||
or have errored out.
|
||||
:return: A list of build id's that are in use
|
||||
"""
|
||||
exclude_build_list = []
|
||||
|
||||
# Gather in use fleets and their builds
|
||||
for fleet in self.get_in_use_fleet_list():
|
||||
exclude_build_list.append(self.get_build_id_from_fleet_id(fleet))
|
||||
|
||||
# Gather any build that is being initialized
|
||||
build_list = self.get_build_id_list(status='INITIALIZED')
|
||||
exclude_build_list.extend(build_list)
|
||||
|
||||
return exclude_build_list
|
||||
|
||||
def get_build_id_list(self, status=None):
|
||||
# type: (str) -> list[str]
|
||||
"""
|
||||
Gets all build id's from the boto3 client. An optional status filter is provided to gather all builds with a
|
||||
given status
|
||||
:param status: A build status to filter. Can be 'READY', 'INITIALIZED', or 'FAILED'
|
||||
:return: a list of build id's
|
||||
"""
|
||||
build_list = []
|
||||
|
||||
# Get paginator
|
||||
try:
|
||||
build_paginator = self.client.get_paginator('list_builds')
|
||||
except ClientError as e:
|
||||
problem = ClientError('Error occurred when getting paginator list_builds. Check to see if the '
|
||||
'botocore version is compatible')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Get iterator
|
||||
try:
|
||||
if status:
|
||||
build_iterator = build_paginator.paginate(Status=status)
|
||||
else:
|
||||
build_iterator = build_paginator.paginate()
|
||||
except ClientError as e:
|
||||
problem = ClientError('Error occurred when getting build list')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Iterate over the build responses
|
||||
try:
|
||||
for build_response in build_iterator:
|
||||
for build_attributes in build_response['Builds']:
|
||||
build_list.append(build_attributes['BuildId'])
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Cannot find Builds in output: {build_response}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
return build_list
|
||||
|
||||
def get_in_use_fleet_list(self):
|
||||
# type: () -> list[str]
|
||||
"""
|
||||
Function that returns a list of in use fleets. 'In use fleets' include those that are being initialized or
|
||||
currently in use in an active game session. This function is used to target stale fleets that are either idle
|
||||
or have errored out.
|
||||
:return: a list of fleet id's
|
||||
"""
|
||||
in_use_fleet_list = set()
|
||||
|
||||
# Gather all fleets
|
||||
fleet_list = self.get_fleet_list()
|
||||
|
||||
# Exclude any fleets that have an "in use" status
|
||||
fleet_attribute_list = self.get_fleet_list_attributes()
|
||||
for fleet_attrib in fleet_attribute_list:
|
||||
if fleet_attrib['Status'] in ["DELETING", "DOWNLOADING", "ACTIVATING", "NEW", "VALIDATING", "BUILDING"]:
|
||||
in_use_fleet_list.add(fleet_attrib['FleetId'])
|
||||
|
||||
# Exclude any fleets that are active, but have players connected to them
|
||||
for fleet_id in fleet_list:
|
||||
game_sessions = self.get_game_sessions(fleet_id)
|
||||
for game_session in game_sessions:
|
||||
if game_session['CurrentPlayerSessionCount'] > 0:
|
||||
in_use_fleet_list.add(fleet_id)
|
||||
|
||||
return list(in_use_fleet_list)
|
||||
|
||||
def get_fleet_list(self):
|
||||
# type: () -> list[str]
|
||||
"""
|
||||
Function to retrieve all fleet Id's from gamelift.
|
||||
:return: A list of fleet ids
|
||||
"""
|
||||
fleet_list = []
|
||||
|
||||
# Get paginator
|
||||
try:
|
||||
fleet_paginator = self.client.get_paginator('list_fleets')
|
||||
except ClientError as e:
|
||||
problem = ClientError('Error occurred when getting paginator list_fleets. Check to see if the '
|
||||
'botocore version is compatible')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Get iterator
|
||||
try:
|
||||
fleet_iterator = fleet_paginator.paginate()
|
||||
except ClientError as e:
|
||||
problem = ClientError('Error occurred when getting fleet list')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
# Iterate over the fleet responses
|
||||
try:
|
||||
for fleet_response in fleet_iterator:
|
||||
for fleet_id in fleet_response['FleetIds']:
|
||||
fleet_list.append(fleet_id)
|
||||
except ValueError as e:
|
||||
problem = ValueError(f'Cannot find FleetIds in output: {fleet_response}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
return fleet_list
|
||||
|
||||
def delete_build_from_gamelift(self, build_id):
|
||||
# type: (str) -> None
|
||||
"""
|
||||
Function to delete a build from gamelift
|
||||
:param build_id: The id of the build given back to us from Gamelift when we uploaded a build
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.client.delete_build(BuildId=build_id)
|
||||
logger.debug(f'Deleting build: {build_id}')
|
||||
except ClientError as e:
|
||||
problem = ClientError(f'Cannot delete build: {build_id}')
|
||||
six.raise_from(problem, e)
|
||||
|
||||
def delete_fleet_from_gamelift(self, fleet_id):
|
||||
# type: (str) -> None
|
||||
"""
|
||||
Function to delete a fleet from gamelift
|
||||
:param fleet_id: The id of the fleet given back to us from Gamelift when we created a fleet
|
||||
:return: None
|
||||
"""
|
||||
# Update fleet capacity command must be ran before deleting the fleet
|
||||
try:
|
||||
self.check_instance_state(fleet_id)
|
||||
self.update_fleet_capacity(fleet_id, 0)
|
||||
# Don't update fleet capacity when fleet status is in ERROR state
|
||||
except AssertionError:
|
||||
logger.debug('Cannot update fleet capacity because fleet is in an ERROR state')
|
||||
|
||||
try:
|
||||
self.client.delete_fleet(FleetId=fleet_id)
|
||||
logger.debug(f"Deleting fleet: {fleet_id}")
|
||||
except ClientError as e:
|
||||
problem = f"Error occurred while attempting to delete fleet: {fleet_id}"
|
||||
six.raise_from(problem, e)
|
||||
|
||||
def update_fleet_capacity(self, fleet_id, desired_instances):
|
||||
# type: (str, int) -> None
|
||||
"""
|
||||
Function to update a fleet capacity in gamelift.
|
||||
:param fleet_id: The id of the fleet given back to us from Gamelift when we created a fleet
|
||||
:param desired_instances: The capacity to set
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.client.update_fleet_capacity(FleetId=fleet_id, DesiredInstances=desired_instances)
|
||||
except ClientError as e:
|
||||
problem = f"Error occurred while trying to update fleet: {fleet_id} to capacity: {desired_instances}"
|
||||
six.raise_from(problem, e)
|
||||
|
||||
def get_gamelift_queue_names(self):
|
||||
"""
|
||||
Calls describe game session queues to list current active queues.
|
||||
:return: list of queue names found
|
||||
"""
|
||||
try:
|
||||
response = self.client.describe_game_session_queues()
|
||||
|
||||
queue_names = []
|
||||
for queue in response['GameSessionQueues']:
|
||||
queue_names.append(queue['Name'])
|
||||
return queue_names
|
||||
except ClientError as e:
|
||||
raise AssertionError('Failed describing gamelift queues, Error:{}'.format(e))
|
||||
|
||||
def delete_gamelift_queue(self, queue_name):
|
||||
"""
|
||||
Deletes a gamelift queue
|
||||
:param queue_name:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
self.client.delete_game_session_queue(
|
||||
Name=queue_name
|
||||
)
|
||||
except ClientError as e:
|
||||
raise AssertionError('Failed deleting queue, Name:{} Error:{}'.format(queue_name, e))
|
||||
|
||||
def get_gamelift_matchmaking_rule_set(self):
|
||||
"""
|
||||
Calls describe matchmaking rule set to list current active rule sets
|
||||
:return: list of rule sets found
|
||||
"""
|
||||
try:
|
||||
response = self.client.describe_matchmaking_rule_sets()
|
||||
rule_set_names = []
|
||||
for rule_set in response['RuleSets']:
|
||||
rule_set_names.append(rule_set['RuleSetName'])
|
||||
return rule_set_names
|
||||
except ClientError as e:
|
||||
raise AssertionError('Failed describing gamelift matchmaking rule sets, Error:{}'.format(e))
|
||||
|
||||
def delete_gamelift_matchmaking_rule_set(self, rule_set_name):
|
||||
"""
|
||||
Deletes a gamelift matchmaking rule set
|
||||
:param rule_set_name:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
self.client.delete_matchmaking_rule_set(
|
||||
Name=rule_set_name
|
||||
)
|
||||
except ClientError as e:
|
||||
raise AssertionError(
|
||||
'Failed deleting gamelift matchmaking rule set, Name:{} Error:{}'.format(rule_set_name, e))
|
||||
|
||||
def get_gamelift_matchmaking_configuration(self):
|
||||
"""
|
||||
Calls describe matchmaking configuration to list current active rule sets
|
||||
:return: list of rule sets found
|
||||
"""
|
||||
try:
|
||||
response = self.client.describe_matchmaking_configurations()
|
||||
configuration_names = []
|
||||
for configuration in response['Configurations']:
|
||||
configuration_names.append(configuration['Name'])
|
||||
return configuration_names
|
||||
except ClientError as e:
|
||||
raise AssertionError('Failed describing gamelift matchmaking configurations, Error:{}'.format(e))
|
||||
|
||||
def delete_gamelift_matchmaking_configurations(self, configuration_name):
|
||||
"""
|
||||
Deletes a gamelift matchmaking configuration
|
||||
:param configuration_name:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
self.client.delete_matchmaking_configuration(
|
||||
Name=configuration_name
|
||||
)
|
||||
except ClientError as e:
|
||||
raise AssertionError(
|
||||
'Failed deleting gamelift matchmaking configuration, Name:{} Error:{}'.format(configuration_name, e))
|
||||
@@ -1,84 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import winreg
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LUMBERYARD_SETTINGS_PATH = r'Software\Amazon\Lumberyard\Settings'
|
||||
|
||||
def set_ly_registry_value(reg_path, value_name, new_value, value_type=winreg.REG_DWORD):
|
||||
"""
|
||||
Sets the specified value for the specified value_name in the LY registry key.
|
||||
:param reg_path: A string that identifies the registry path to the desired key (e.g. Software\Amazon\Lumberyard\Settings)
|
||||
:param value_name: A string that identifies the value name (e.g. UndoLevels, ViewportInteractionModel)
|
||||
:param new_value: Value to set on the specified value_name
|
||||
:param value_type: The type of value set. Defaults to a 32-bit number.
|
||||
:return: None
|
||||
"""
|
||||
# Open LY Registry key
|
||||
try:
|
||||
key = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, reg_path, 0, access=winreg.KEY_ALL_ACCESS)
|
||||
except OSError as err:
|
||||
logger.error(err)
|
||||
|
||||
# Set value_name to the specified value
|
||||
winreg.SetValueEx(key, value_name, 0, value_type, new_value)
|
||||
|
||||
# Verify the value set to the specified value_name
|
||||
value = winreg.QueryValueEx(key, value_name)
|
||||
if new_value == value[0]:
|
||||
logger.debug(f'Successfully set {value_name} to {value[0]}')
|
||||
else:
|
||||
logger.debug(f'Failed to set {value_name} to {new_value}. Current value is {value[0]}.')
|
||||
|
||||
|
||||
def get_ly_registry_value(reg_path, value_name):
|
||||
"""
|
||||
Gets the current value for an existing value_name in the LY registry key.
|
||||
:param reg_path: A string that identifies the registry path to the desired key (e.g. Software\Amazon\Lumberyard\Settings)
|
||||
:param value_name: A string that identifies the value name (e.g. UndoLevels, ViewportInteractionModel)
|
||||
:return: Value set for the specified value_name
|
||||
"""
|
||||
# Open LY Registry key
|
||||
try:
|
||||
key = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, reg_path, 0, access=winreg.KEY_ALL_ACCESS)
|
||||
except OSError as err:
|
||||
logger.error(err)
|
||||
|
||||
# Check if value name is present and return current value
|
||||
try:
|
||||
value_name_value = winreg.QueryValueEx(key, value_name)
|
||||
logger.debug(f'{value_name} is {value_name_value[0]}')
|
||||
return value_name_value[0]
|
||||
except OSError as err:
|
||||
logger.error(err)
|
||||
|
||||
|
||||
def delete_ly_registry_value(reg_path, value_name):
|
||||
"""
|
||||
Deletes the specific registry value_name found in the reg_path key.
|
||||
:param reg_path: A string that identifies the registry path to the desired key (e.g. Software\Amazon\Lumberyard\Settings)
|
||||
:param value_name: A string that identifies the value name (e.g. UndoLevels, ViewportInteractionModel)
|
||||
:return: None
|
||||
"""
|
||||
# Open LY Registry key
|
||||
try:
|
||||
key = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, reg_path, 0, access=winreg.KEY_ALL_ACCESS)
|
||||
except OSError as err:
|
||||
logger.error(err)
|
||||
|
||||
# Attempt to delete the specified key/value
|
||||
try:
|
||||
winreg.DeleteValue(key, value_name)
|
||||
except OSError as err:
|
||||
logger.error(err)
|
||||
@@ -1,10 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
@@ -1,156 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
PreRequisites:
|
||||
-AWS Credentials are set using AWS
|
||||
configure
|
||||
To Run:
|
||||
cd ~/dev/Tests
|
||||
~/Python/python3.cmd -m networking.clean_gamelift_builds_and_fleets
|
||||
"""
|
||||
|
||||
import logging
|
||||
import boto3
|
||||
|
||||
from ly_shared import gamelift_utils
|
||||
from .test_lib import networking_constants
|
||||
|
||||
logger = logging.basicConfig(filename='test.log', filemode='w', level=logging.DEBUG)
|
||||
AWS_PROFILE = networking_constants.DEFAULT_PROFILE
|
||||
REGION = networking_constants.DEFAULT_REGION
|
||||
BOTO3_SESSION = boto3.session.Session(profile_name=AWS_PROFILE, region_name=REGION)
|
||||
GAMELIFT_UTILS = gamelift_utils.GameliftUtils(BOTO3_SESSION)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
This script cleans up the builds and fleets from gamelift. The main method retrieves a list of build and fleet
|
||||
ids, and then sends a console command to delete them. The script will exclude any builds and fleets that are
|
||||
currently in use. It will then validate to make sure all non excluded builds and fleets are deleted.
|
||||
"""
|
||||
print("Starting Gamelift cleanup script")
|
||||
fleet_list = _get_unused_fleets_to_delete()
|
||||
build_list = _get_builds_to_delete()
|
||||
|
||||
queue_list = GAMELIFT_UTILS.get_gamelift_queue_names()
|
||||
configuration_list = GAMELIFT_UTILS.get_gamelift_matchmaking_configuration()
|
||||
|
||||
for configuration in configuration_list:
|
||||
GAMELIFT_UTILS.delete_gamelift_matchmaking_configurations(configuration)
|
||||
|
||||
assert validate_matchmaking_configuration_list_empty(), 'Matchmaking configuration list was not deleted properly!'
|
||||
|
||||
rule_set_list = GAMELIFT_UTILS.get_gamelift_matchmaking_rule_set()
|
||||
for rule_set in rule_set_list:
|
||||
GAMELIFT_UTILS.delete_gamelift_matchmaking_rule_set(rule_set)
|
||||
|
||||
assert validate_matchmaking_rule_set_list_empty(), 'Matchmaking rule set list was not deleted properly!'
|
||||
|
||||
for queue in queue_list:
|
||||
GAMELIFT_UTILS.delete_gamelift_queue(queue)
|
||||
|
||||
assert validate_queue_list_empty(), 'queue list was not deleted properly!'
|
||||
|
||||
for fleet in fleet_list:
|
||||
GAMELIFT_UTILS.delete_fleet_from_gamelift(fleet)
|
||||
|
||||
assert validate_fleet_list_deleted(fleet_list), 'Fleet list was not deleted properly!'
|
||||
|
||||
for build_id in build_list:
|
||||
GAMELIFT_UTILS.delete_build_from_gamelift(build_id)
|
||||
|
||||
assert validate_build_list_deleted(build_list), 'Build list was not deleted properly!'
|
||||
|
||||
|
||||
def _get_unused_fleets_to_delete():
|
||||
"""
|
||||
Gathers all fleets and removes any in use fleets from the list. This results in a list of fleet id's to delete
|
||||
:return: a list of fleet ids
|
||||
"""
|
||||
fleet_id_list = GAMELIFT_UTILS.get_fleet_list()
|
||||
for fleet_id in GAMELIFT_UTILS.get_in_use_fleet_list():
|
||||
try:
|
||||
fleet_id_list.remove(fleet_id)
|
||||
except ValueError:
|
||||
pass # Try to remove any excluded fleets from our list
|
||||
return fleet_id_list
|
||||
|
||||
|
||||
def _get_builds_to_delete():
|
||||
"""
|
||||
Gathers all builds and removes any in use builds from the list. This results in a list of build id's to delete
|
||||
:return: a list of build ids
|
||||
"""
|
||||
build_id_list = GAMELIFT_UTILS.get_build_id_list()
|
||||
for build_id in GAMELIFT_UTILS.get_in_use_build_list_from_gamelift():
|
||||
try:
|
||||
build_id_list.remove(build_id)
|
||||
except ValueError:
|
||||
pass # Try to remove any excluded builds from our list
|
||||
return build_id_list
|
||||
|
||||
|
||||
def validate_build_list_deleted(exclude_build_list):
|
||||
"""
|
||||
Validates whether the builds were deleted correctly
|
||||
:param exclude_build_list: a list of builds that should not be deleted
|
||||
:return: returns True if the builds are deleted correctly
|
||||
"""
|
||||
build_list = GAMELIFT_UTILS.get_build_id_list()
|
||||
|
||||
for build in build_list:
|
||||
if build in exclude_build_list:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def validate_fleet_list_deleted(exclude_fleet_list):
|
||||
"""
|
||||
Validates whether the fleets were deleted correctly
|
||||
:param exclude_fleet_list: a list of fleets that should not be deleted
|
||||
:return: returns True if the fleets are deleted correctly
|
||||
"""
|
||||
fleet_attribute_list = GAMELIFT_UTILS.get_fleet_list_attributes()
|
||||
|
||||
for fleet_attrib in fleet_attribute_list:
|
||||
if (fleet_attrib['Status'] != "DELETING") and (fleet_attrib['FleetId'] in exclude_fleet_list):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def validate_queue_list_empty():
|
||||
"""
|
||||
Verifies there are no gamelift queues.
|
||||
:return: bool True: If no queues.
|
||||
"""
|
||||
queue_list = GAMELIFT_UTILS.get_gamelift_queue_names()
|
||||
return len(queue_list) == 0
|
||||
|
||||
|
||||
def validate_matchmaking_configuration_list_empty():
|
||||
"""
|
||||
Verifies there are no gamelift matchmaking configurations.
|
||||
:return: bool True: If no queues.
|
||||
"""
|
||||
configuration_list = GAMELIFT_UTILS.get_gamelift_matchmaking_configuration()
|
||||
return len(configuration_list) == 0
|
||||
|
||||
|
||||
def validate_matchmaking_rule_set_list_empty():
|
||||
"""
|
||||
Verifies there are no gamelift matchmaking rule sets.
|
||||
:return: bool True: If no queues.
|
||||
"""
|
||||
rule_set_list = GAMELIFT_UTILS.get_gamelift_matchmaking_rule_set()
|
||||
return len(rule_set_list) == 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
PRE-REQUISITES:
|
||||
-machine is configured with AWS credentials (run aws configure)
|
||||
-AWS default output format is set to 'json'
|
||||
-MultiplayerSample is built for the given platforms
|
||||
-Dedicated servers are also built for the respective platforms
|
||||
-Have LYTestTools installed - run in dev 'lmbr_test pysetup install ly_test_tools'
|
||||
-A GameLift fleet is ready with its fleet id provided in the config.ini file
|
||||
|
||||
Part 3 of 3 for Linux GameLift Automation:
|
||||
Launches 4 MultiplayerSample clients and connects to a given GameLift fleet
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
import configparser
|
||||
import logging
|
||||
import shutil
|
||||
pytest.importorskip('dateutil')
|
||||
import boto3
|
||||
|
||||
pytest.importorskip("ly_test_tools")
|
||||
# ly_test_tools dependencies
|
||||
import ly_test_tools.builtin.helpers as helpers
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
import ly_test_tools.launchers.launcher_helper as launcher_helper
|
||||
import ly_remote_console.remote_console_commands as remote_console
|
||||
|
||||
# test level dependencies
|
||||
from ...ly_shared import file_utils, network_utils
|
||||
from ...ly_shared.gamelift_utils import GameliftUtils
|
||||
from ..test_lib import networking_constants
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
INTERVAL_TIME = 10 # in seconds, longer interval times to reduce api call spam
|
||||
TIMEOUT = 30 # in seconds, time to wait for asyncronous api calls
|
||||
CONFIG_SECTION = networking_constants.LINUX_GAMELIFT_WORKFLOW_CONFIG_SECTION
|
||||
CONFIG_FILENAME = networking_constants.LINUX_GAMELIFT_WORKFLOW_CONFIG_FILENAME
|
||||
CONFIG_FLEET_KEY = networking_constants.LINUX_GAMELIFT_WORKFLOW_CONFIG_FLEET_KEY
|
||||
CONFIG_DIR = os.path.dirname(__file__) # assumes CONFIG_FILENAME is a sibling to this file
|
||||
CONFIG_FILE = os.path.join(CONFIG_DIR, CONFIG_FILENAME)
|
||||
AWS_PROFILE = networking_constants.DEFAULT_PROFILE
|
||||
REGION = networking_constants.DEFAULT_REGION
|
||||
BOTO3_SESSION = boto3.session.Session(profile_name=AWS_PROFILE, region_name=REGION)
|
||||
GAMELIFTUTILS = GameliftUtils(BOTO3_SESSION)
|
||||
REMOTE_CONSOLE_PORT = 4600
|
||||
LAUNCHER_PORT_DICTS = []
|
||||
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multiplayer_client_launcher_list(request, workspace, level, number_of_launchers):
|
||||
# type: (ly_test_tools.managers.workspace.AbstractWorkspaceManager, str, int) -> test_tools.launchers.platforms.base.Launcher
|
||||
"""
|
||||
Create a list of simple launchers. Returns a list of dicts that contain the launcher and its remote console port.
|
||||
ex.
|
||||
for launcher_dict in launcher_list:
|
||||
game_launcher = launcher_dict['launcher']
|
||||
game_port = launcher_dict['port']
|
||||
:param request: Pytest request object
|
||||
:param workspace: The workspace where the launcher is located
|
||||
:param level: The level name to load
|
||||
:param number_of_launchers: The number of launcher instances
|
||||
|
||||
:return: A fully configured list of dicts containing launchers and their respective port
|
||||
"""
|
||||
launcher_list = []
|
||||
|
||||
def teardown():
|
||||
for launcher_dict in launcher_list:
|
||||
if launcher_dict['launcher'].is_alive():
|
||||
launcher_dict['launcher'].stop()
|
||||
if launcher_dict['remote_console'].connected:
|
||||
launcher_dict['remote_console'].stop()
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
# Create a list of launchers
|
||||
for index in range(0, number_of_launchers):
|
||||
port = REMOTE_CONSOLE_PORT + index
|
||||
launcher_list.append({'launcher': launcher_helper.create_launcher(workspace),
|
||||
'rc_port': port,
|
||||
'remote_console': remote_console.RemoteConsole(port=port)})
|
||||
|
||||
return launcher_list
|
||||
|
||||
|
||||
@pytest.mark.parametrize('platform', ['win_x64_vs2017'])
|
||||
@pytest.mark.parametrize('configuration', ['profile'])
|
||||
@pytest.mark.parametrize('project', ['MultiplayerSample'])
|
||||
@pytest.mark.parametrize('spec', ['all'])
|
||||
@pytest.mark.parametrize('level', ['multiplayersample'])
|
||||
@pytest.mark.parametrize('number_of_launchers', [4])
|
||||
class TestRunWindowsGameliftOnLinuxFleet(object):
|
||||
"""
|
||||
This test will connect 4 MultiplayerSample clients to a given fleet.
|
||||
"""
|
||||
def test_Gamelift_WindowsClientsConnectLinuxFleet_Workflow(self, request, multiplayer_client_launcher_list,
|
||||
workspace):
|
||||
def teardown():
|
||||
# Gather all log files in cache/user/log
|
||||
for file_name in os.listdir(workspace.paths.project_log()):
|
||||
file_path = os.path.join(workspace.paths.project_log(), file_name)
|
||||
try:
|
||||
workspace.artifact_manager.save_artifact(file_path)
|
||||
except(IOError, WindowsError):
|
||||
logger.debug(f'A log could not be saved: {file_path}')
|
||||
|
||||
# If error or crash logs exist, move them to the artifact
|
||||
file_utils.gather_error_logs(workspace)
|
||||
|
||||
# Required to use custom teardown()
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
# Delete previous logs
|
||||
if os.path.exists(workspace.paths.project_log()):
|
||||
shutil.rmtree(workspace.paths.project_log())
|
||||
|
||||
# Test setup
|
||||
current_launcher_instance = 0
|
||||
gamelift_credentials = GameliftUtils.get_aws_credentials(AWS_PROFILE)
|
||||
|
||||
# Get the fleet id
|
||||
config = configparser.ConfigParser()
|
||||
config.read(CONFIG_FILE)
|
||||
try:
|
||||
fleet_id = config[CONFIG_SECTION][CONFIG_FLEET_KEY]
|
||||
except KeyError as e:
|
||||
raise AssertionError(f'Cannot find section: {CONFIG_SECTION} or fleet key: {CONFIG_FLEET_KEY} in file: '
|
||||
f'{CONFIG_FILE}. {e}')
|
||||
|
||||
host_launcher = multiplayer_client_launcher_list[0]
|
||||
host_launcher['launcher'].args = ['+sv_port', '33435', '+gamelift_fleet_id', fleet_id,
|
||||
'+gamelift_aws_access_key', gamelift_credentials['access'],
|
||||
'+gamelift_aws_secret_key', gamelift_credentials['secret'],
|
||||
'+gamelift_aws_region', REGION, '+gamelift_endpoint',
|
||||
f'gamelift.{REGION}.amazonaws.com', '+map', 'gamelobby']
|
||||
|
||||
# Process Assets
|
||||
assert workspace.asset_processor.batch_process(), 'Assets did not process correctly when calling AP Batch'
|
||||
|
||||
# Normally we start a launcher using with: which utilizes context managers to ensure teardown
|
||||
# However we are using multiple launchers so we ensure launcher.stop() is called in custom teardown
|
||||
host_launcher['launcher'].start()
|
||||
|
||||
# Start the remote console
|
||||
host_launcher['remote_console'].start()
|
||||
|
||||
# Wait for the launcher to load
|
||||
validate_level_loaded = "SetGlobalState 13->2 'LEVEL_LOAD_COMPLETE' -> 'RUNNING'"
|
||||
host_launcher['remote_console'].expect_log_line(validate_level_loaded, TIMEOUT)
|
||||
|
||||
# Checks if the remote console port is listening
|
||||
waiter.wait_for(lambda: network_utils.check_for_listening_port(host_launcher['rc_port']),
|
||||
timeout=TIMEOUT,
|
||||
exc=AssertionError(f'Port {host_launcher["rc_port"]} not listening.'))
|
||||
|
||||
# Set expected log line to assert
|
||||
gamelift_host_event = host_launcher['remote_console'].\
|
||||
expect_log_line('(GameLift) - Initialized GameLift client successfully.', TIMEOUT)
|
||||
|
||||
# Send the remote console command to host a multiplayersample game
|
||||
host_launcher['remote_console'].\
|
||||
send_command('gamelift_host AutomationTest multiplayersample 8')
|
||||
|
||||
assert gamelift_host_event(), 'Console never stated gamelift hosting was completed properly'
|
||||
|
||||
# Wait for the game to load
|
||||
host_launcher['remote_console'].expect_log_line(validate_level_loaded, TIMEOUT)
|
||||
host_launcher['remote_console'].stop()
|
||||
current_launcher_instance += 1
|
||||
|
||||
# Assert on if our client connected
|
||||
waiter.wait_for(lambda: GAMELIFTUTILS.check_all_current_player_sessions(fleet_id, current_launcher_instance),
|
||||
timeout=TIMEOUT, interval=INTERVAL_TIME,
|
||||
exc=AssertionError(f"Client {current_launcher_instance} never connected to GameLift"))
|
||||
|
||||
# Repeat with all other clients, except the console command is different
|
||||
for launcher_dict in multiplayer_client_launcher_list[1:]:
|
||||
|
||||
launcher_dict['launcher'].args = ['+sv_port', '33435', '+gamelift_fleet_id', fleet_id,
|
||||
'+gamelift_aws_access_key', gamelift_credentials['access'],
|
||||
'+gamelift_aws_secret_key', gamelift_credentials['secret'],
|
||||
'+gamelift_aws_region', REGION, '+gamelift_endpoint',
|
||||
f'gamelift.{REGION}.amazonaws.com', '+gm_enableMetrics']
|
||||
|
||||
launcher_dict['launcher'].start()
|
||||
launcher_dict['remote_console'].start()
|
||||
launcher_dict['remote_console'].expect_log_line(validate_level_loaded, TIMEOUT)
|
||||
launcher_dict['remote_console'].send_command('gamelift_join 1')
|
||||
launcher_dict['remote_console'].expect_log_line(validate_level_loaded, TIMEOUT)
|
||||
|
||||
waiter.wait_for(
|
||||
lambda: GAMELIFTUTILS.check_all_current_player_sessions(fleet_id, current_launcher_instance + 1),
|
||||
timeout=TIMEOUT, interval=INTERVAL_TIME,
|
||||
exc=AssertionError(f"Client {current_launcher_instance} never connected to GameLift"))
|
||||
|
||||
waiter.wait_for(lambda: network_utils.check_for_listening_port(launcher_dict['rc_port']),
|
||||
timeout=TIMEOUT, exc=AssertionError(f'Port {launcher_dict["rc_port"]} not listening.'))
|
||||
|
||||
launcher_dict['remote_console'].stop()
|
||||
current_launcher_instance += 1
|
||||
|
||||
current_launcher_instance = 0
|
||||
for launcher_dict in multiplayer_client_launcher_list:
|
||||
assert launcher_dict['launcher'].is_alive(), f"Client number: {current_launcher_instance} was closed " \
|
||||
f"unexpectedly"
|
||||
current_launcher_instance += 1
|
||||
@@ -1,81 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
PRE-REQUISITES:
|
||||
-machine is configured with AWS credentials (run aws configure)
|
||||
-MultiplayerSample is built for the given platforms
|
||||
-Dedicated servers are also built for the respective platforms
|
||||
-Have LYTestTools installed - run in dev 'lmbr_test pysetup install ly_test_tools'
|
||||
|
||||
Part 1 of 3 for Linux Gamelift Automation:
|
||||
Prepares a Linux Gamelift package and uploads it to S3
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
import logging
|
||||
import configparser
|
||||
pytest.importorskip('boto3')
|
||||
import boto3
|
||||
|
||||
pytest.importorskip("ly_test_tools")
|
||||
# ly_test_tools dependencies
|
||||
import ly_test_tools.builtin.helpers as helpers
|
||||
|
||||
# test level dependencies
|
||||
from ...ly_shared import s3_utils, gamelift_utils, file_utils
|
||||
from ..test_lib import networking_constants
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
AWS_PROFILE = networking_constants.DEFAULT_PROFILE
|
||||
REGION = networking_constants.DEFAULT_REGION
|
||||
CONFIG_SECTION = networking_constants.LINUX_GAMELIFT_WORKFLOW_CONFIG_SECTION
|
||||
CONFIG_FILENAME = networking_constants.LINUX_GAMELIFT_WORKFLOW_CONFIG_FILENAME
|
||||
CONFIG_TARFILE_KEY = networking_constants.LINUX_GAMELIFT_WORKFLOW_CONFIG_TARFILE_KEY
|
||||
CONFIG_DIR = os.path.dirname(__file__) # assumes CONFIG_FILENAME is a sibling to this file
|
||||
CONFIG_FILE = os.path.join(CONFIG_DIR, CONFIG_FILENAME)
|
||||
BOTO3_SESSION = boto3.session.Session(profile_name=AWS_PROFILE, region_name=REGION)
|
||||
S3_UTIL = s3_utils.S3Utils(BOTO3_SESSION)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('platform', ['win_x64_vs2017'])
|
||||
@pytest.mark.parametrize('configuration', ['profile'])
|
||||
@pytest.mark.parametrize('project', ['MultiplayerSample'])
|
||||
@pytest.mark.parametrize('spec', ['all'])
|
||||
class TestGameliftLinuxSetup(object):
|
||||
"""
|
||||
This test will create a Linux Gamelift package and upload it to s3
|
||||
"""
|
||||
def test_Gamelift_PrepareLinuxPackage_Workflow(self, request, workspace):
|
||||
def teardown():
|
||||
# Delete the locally generated tar file
|
||||
if os.path.exists(tar_filepath):
|
||||
os.remove(tar_filepath)
|
||||
|
||||
# Required to use custom teardown()
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
# Run paks script
|
||||
gamelift_utils.GameliftUtils.run_multiplayer_sample_paks_pc_dedicated(workspace.paths.dev())
|
||||
|
||||
# Run linux packer script
|
||||
tar_filepath = gamelift_utils.GameliftUtils.run_multiplayer_sample_linux_packer(workspace.paths.dev())
|
||||
tar_filename = os.path.basename(tar_filepath)
|
||||
|
||||
# Upload to s3
|
||||
S3_UTIL.upload_to_bucket(networking_constants.LINUX_TAR_BUCKET, tar_filepath)
|
||||
|
||||
# Write filename to .ini file to be utilized in next script
|
||||
file_utils.clear_out_file(CONFIG_FILE)
|
||||
config = configparser.ConfigParser()
|
||||
config[CONFIG_SECTION] = {}
|
||||
config[CONFIG_SECTION][CONFIG_TARFILE_KEY] = tar_filename
|
||||
|
||||
with open(CONFIG_FILE, 'w') as config_to_write:
|
||||
config.write(config_to_write)
|
||||
@@ -1,10 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
@@ -1,99 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
import os
|
||||
|
||||
DEFAULT_REGION = 'us-west-2'
|
||||
DEFAULT_PROFILE = 'LYQA-Gamelift'
|
||||
LINUX_TAR_BUCKET = 'ly-gamelift-linux-packages'
|
||||
DEFAULT_LINUX_WORKSPACE = os.path.join('/home', 'ubuntu', 'workspace')
|
||||
LINUX_GAMELIFT_WORKFLOW_CONFIG_FILENAME = 'gamelift_workflow_config.ini'
|
||||
LINUX_GAMELIFT_WORKFLOW_CONFIG_SECTION = 'LinuxGameLiftTest'
|
||||
LINUX_GAMELIFT_WORKFLOW_CONFIG_TARFILE_KEY = 'TAR_FILENAME'
|
||||
LINUX_GAMELIFT_WORKFLOW_CONFIG_FLEET_KEY = 'FLEET_ID'
|
||||
WINDOWS_GAMELIFT_FLEET = {
|
||||
'fleet_name': 'WindowsGameliftFleet',
|
||||
'ec2_instance_type': 'c4.large',
|
||||
'runtime_configuration': {
|
||||
'ServerProcesses': [
|
||||
{
|
||||
'LaunchPath': None, # Override this value with os.path.join("C:", "game", <BIN.DEDICATED_DIR>,
|
||||
# "MultiplayerSampleLauncher_Server.exe")
|
||||
'Parameters': '+sv_port 33435 +gamelift_start_server',
|
||||
'ConcurrentExecutions': 1,
|
||||
},
|
||||
{
|
||||
'LaunchPath': None,
|
||||
'Parameters': '+sv_port 33436 +gamelift_start_server',
|
||||
'ConcurrentExecutions': 1,
|
||||
},
|
||||
{
|
||||
'LaunchPath': None,
|
||||
'Parameters': '+sv_port 33437 +gamelift_start_server',
|
||||
'ConcurrentExecutions': 1,
|
||||
}
|
||||
]
|
||||
},
|
||||
'ec2_inbound_permissions': [ # Ports & IP's for clients to connect
|
||||
{
|
||||
'FromPort': 33435,
|
||||
'ToPort': 33437,
|
||||
'IpRange': '0.0.0.0/0', # Override this value with client IP
|
||||
'Protocol': 'UDP',
|
||||
}
|
||||
]
|
||||
}
|
||||
WINDOWS_CUSTOMBACKFILL_GAMELIFT_FLEET = {
|
||||
'fleet_name': 'WindowsGameliftFleetCustomBackfill',
|
||||
'ec2_instance_type': 'c4.large',
|
||||
'runtime_configuration': {
|
||||
'ServerProcesses': [
|
||||
{
|
||||
'LaunchPath': None, # Override this value with os.path.join("C:", "game", <BIN.DEDICATED_DIR>,
|
||||
# "MultiplayerSampleLauncher_Server.exe")
|
||||
'Parameters': '+sv_port 33435 +gamelift_start_server +gamelift_flexmatch_enable 1 '
|
||||
'+gamelift_flexmatch_onplayerremoved_enable 1 '
|
||||
'+gamelift_flexmatch_minimumplayersessioncount 2',
|
||||
'ConcurrentExecutions': 1,
|
||||
}
|
||||
]
|
||||
},
|
||||
'ec2_inbound_permissions': [ # Ports & IP's for clients to connect
|
||||
{
|
||||
'FromPort': 33435,
|
||||
'ToPort': 33435,
|
||||
'IpRange': '0.0.0.0/0', # Override this value with client IP
|
||||
'Protocol': 'UDP',
|
||||
}
|
||||
]
|
||||
}
|
||||
LINUX_GAMELIFT_FLEET = {
|
||||
'fleet_name': 'LinuxGameliftFleet',
|
||||
'ec2_instance_type': 'c5.large',
|
||||
'runtime_configuration': {
|
||||
"GameSessionActivationTimeoutSeconds": 300,
|
||||
"MaxConcurrentGameSessionActivations": 3,
|
||||
"ServerProcesses": [
|
||||
{
|
||||
"LaunchPath": "/local/game/./MultiplayerSampleLauncher_Server",
|
||||
"Parameters": "+sv_port 33435 +map multiplayersample +gamelift_start_server true",
|
||||
"ConcurrentExecutions": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
'ec2_inbound_permissions': [ # Ports & IP's for clients to connect
|
||||
{
|
||||
'FromPort': 33435,
|
||||
'ToPort': 33437,
|
||||
'IpRange': '0.0.0.0/0', # Override this value with client IP
|
||||
'Protocol': 'UDP',
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
@@ -1,547 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
PRE-REQUISITES:
|
||||
-machine is configured with AWS credentials (run aws configure)
|
||||
-AWS default output format is set to 'json'
|
||||
-MultiplayerSample is built for the given platforms
|
||||
-Dedicated servers are also built for the respective platforms
|
||||
-Have LYTestTools installed - run in dev 'lmbr_test pysetup install ly_test_tools'
|
||||
|
||||
All of these tests will test the ability for 4 clients ability to attach to a Gamelift server instance. We will also
|
||||
check that all of the game clients are alive and connected by the end of the tests. If an assertion happens we will
|
||||
gather all of the artifacts and move them into an artifact folder which will eventually be consumed by Flume.
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
pytest.importorskip("boto3")
|
||||
import boto3
|
||||
|
||||
pytest.importorskip("ly_test_tools")
|
||||
# ly_test_tools dependencies
|
||||
import ly_test_tools.builtin.helpers as helpers
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
import ly_test_tools.launchers.launcher_helper as launcher_helper
|
||||
import ly_remote_console.remote_console_commands
|
||||
|
||||
# test level dependencies
|
||||
from ...ly_shared import file_utils, network_utils
|
||||
from ...ly_shared.gamelift_utils import GameliftUtils
|
||||
from ..test_lib import networking_constants
|
||||
|
||||
import uuid
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
INTERVAL_TIME = 10 # seconds for api calls against aws
|
||||
AWS_PROFILE = networking_constants.DEFAULT_PROFILE
|
||||
REGION = networking_constants.DEFAULT_REGION
|
||||
BOTO3_SESSION = boto3.session.Session(profile_name=AWS_PROFILE, region_name=REGION)
|
||||
FLEET_TIMEOUT = 3600
|
||||
gamelift_utils = GameliftUtils(BOTO3_SESSION)
|
||||
|
||||
AWS_REGION = 'us-west-2'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def remote_console_instances(request,
|
||||
number_of_launchers # type: int
|
||||
):
|
||||
"""
|
||||
Creates a list of RemoteConsole objects
|
||||
|
||||
:param number_of_launchers: The number of launcher instances
|
||||
:return: A list of RemoteConsole objects
|
||||
"""
|
||||
# Each launcher has its own remote console
|
||||
remote_console_list = []
|
||||
for x in range(0, number_of_launchers):
|
||||
remote_console_list.append(ly_remote_console.remote_console_commands.RemoteConsole(port=4600+x))
|
||||
|
||||
|
||||
def teardown():
|
||||
for console in remote_console_list:
|
||||
if console.connected:
|
||||
console.stop()
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return remote_console_list
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multiplayer_client_launcher_list(
|
||||
request,
|
||||
workspace, # type: test_tools.managers.workspace.AbstractWorkspaceManager
|
||||
level, # type: str
|
||||
number_of_launchers, # type: int
|
||||
):
|
||||
# type: (...) -> test_tools.launchers.platforms.base.Launcher
|
||||
"""
|
||||
Create a list of simple launchers.
|
||||
|
||||
:param workspace: The workspace where the launcher is located
|
||||
:param level: The level name to load
|
||||
:param number_of_launchers: The number of launcher instances
|
||||
|
||||
:return: A fully configured list of launchers
|
||||
"""
|
||||
launcher_list = []
|
||||
|
||||
# Create a list of launchers
|
||||
for _ in range(0, number_of_launchers):
|
||||
launcher_list.append(launcher_helper.create_launcher(workspace))
|
||||
|
||||
def teardown():
|
||||
# Gather all log files in cache/user/log
|
||||
try:
|
||||
for file_name in os.listdir(workspace.paths.project_log()):
|
||||
file_path = os.path.join(workspace.paths.project_log(), file_name)
|
||||
try:
|
||||
workspace.artifact_manager.save_artifact(file_path)
|
||||
except(IOError, WindowsError):
|
||||
logger.info('A log could not be saved: {}'.format(file_path))
|
||||
except:
|
||||
logger.info("No log folder found for path: {}".format(workspace.paths.project_log()))
|
||||
|
||||
# If error or crash logs exist, move them to the artifact
|
||||
file_utils.gather_error_logs(workspace)
|
||||
|
||||
# Custom teardown of multiple launchers because we cannot use context manager
|
||||
for client in launcher_list:
|
||||
try:
|
||||
client.stop()
|
||||
except AssertionError:
|
||||
logger.info("A launcher could not be shut down!")
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return launcher_list
|
||||
|
||||
def add_game_lift_creds_and_endpoint_args(launcher, gamelift_credentials):
|
||||
launcher.args.extend(['+gamelift_aws_access_key', gamelift_credentials['access'],
|
||||
'+gamelift_aws_secret_key', gamelift_credentials['secret'],
|
||||
'+gamelift_aws_region', AWS_REGION,
|
||||
'+gamelift_endpoint', 'gamelift.{}.amazonaws.com'.format(AWS_REGION)])
|
||||
|
||||
|
||||
def create_game_lift_player_sessions(fleet_id, gamelift_credentials, timeout,
|
||||
remote_console_instances, multiplayer_client_launcher_list):
|
||||
"""
|
||||
Creates Gamelift player sessions. Connects the MultiplayerSample game client to game sessions using remote console
|
||||
gamelift_join command. The command supports joining first game session found in the list of game sessions from
|
||||
GameLift
|
||||
:param fleet_id: Fleet to create player sessions
|
||||
:param gamelift_credentials: Credentials to use to make GameLift calls
|
||||
:param timeout: Timeout for waiter calls
|
||||
:param remote_console_instances: Remote console instances to work with
|
||||
:param multiplayer_client_launcher_list: Client launcher instances to work with
|
||||
:return:
|
||||
"""
|
||||
# First 2 instances are reserved for creating game sessions. 3 and 4 instance of the launcher and remote console instances
|
||||
# are used for player sessions
|
||||
current_launcher_instance = 2
|
||||
|
||||
while current_launcher_instance < 4:
|
||||
launcher = multiplayer_client_launcher_list[current_launcher_instance]
|
||||
launcher.args = []
|
||||
add_game_lift_creds_and_endpoint_args(launcher, gamelift_credentials)
|
||||
launcher.args.extend(['+gamelift_fleet_id', fleet_id, '+gm_enableMetrics'])
|
||||
|
||||
launcher.start()
|
||||
|
||||
# Hard wait for the clients to initialize. Hoping 10secs is enough on the worst machines as well.
|
||||
time.sleep(10)
|
||||
|
||||
join_game_session_index = current_launcher_instance % 2 + 1
|
||||
remote_console_instances[current_launcher_instance].start()
|
||||
remote_console_instances[current_launcher_instance].send_command('gamelift_join {}'
|
||||
.format(join_game_session_index))
|
||||
|
||||
waiter.wait_for(lambda: network_utils.check_for_listening_port(4600 + current_launcher_instance),
|
||||
timeout=timeout,
|
||||
exc=AssertionError('Port 460{} not listening.'.format(current_launcher_instance)))
|
||||
|
||||
remote_console_instances[current_launcher_instance].stop()
|
||||
current_launcher_instance += 1
|
||||
|
||||
waiter.wait_for(
|
||||
lambda: gamelift_utils.check_total_players_in_all_game_sessions(fleet_id, current_launcher_instance),
|
||||
timeout=timeout, interval=5,
|
||||
exc=AssertionError("Client {} never connected".format(current_launcher_instance)))
|
||||
|
||||
|
||||
def create_gamelift_flexmatch_game_and_player_sessions(matchmaking_config_name, fleet_id, gamelift_credentials, timeout,
|
||||
remote_console_instances, multiplayer_client_launcher_list):
|
||||
# First 2 instances are reserved for creating game sessions. 3 and 4 for player sessions.
|
||||
# 5 and 6 instance are used for flex match.
|
||||
# are used for player sessions
|
||||
current_launcher_instance = 4
|
||||
|
||||
multiplayer_client_launcher_count = len(multiplayer_client_launcher_list)
|
||||
while current_launcher_instance < multiplayer_client_launcher_count:
|
||||
launcher = multiplayer_client_launcher_list[current_launcher_instance]
|
||||
launcher.args = []
|
||||
add_game_lift_creds_and_endpoint_args(launcher, gamelift_credentials)
|
||||
launcher.start()
|
||||
|
||||
# Hard wait for the clients to initialize. Hoping 10secs is enough on the worst machines as well.
|
||||
time.sleep(10)
|
||||
|
||||
remote_console_instances[current_launcher_instance].start()
|
||||
|
||||
waiter.wait_for(lambda: network_utils.check_for_listening_port(4600 + current_launcher_instance),
|
||||
timeout=timeout,
|
||||
exc=AssertionError('Port 460{} not listening.'.format(current_launcher_instance)))
|
||||
current_launcher_instance += 1
|
||||
|
||||
current_launcher_instance = 4
|
||||
while current_launcher_instance < multiplayer_client_launcher_count:
|
||||
remote_console_instances[current_launcher_instance].send_command('gamelift_flexmatch {}'
|
||||
.format(matchmaking_config_name))
|
||||
|
||||
remote_console_instances[current_launcher_instance].stop()
|
||||
current_launcher_instance += 1
|
||||
|
||||
waiter.wait_for(
|
||||
lambda: gamelift_utils.check_total_players_in_all_game_sessions(fleet_id, multiplayer_client_launcher_count),
|
||||
timeout=timeout, interval=5,
|
||||
exc=AssertionError("Client 5 and 6 never connected"))
|
||||
|
||||
def create_gamelift_custom_flexmatch_game_and_player_sessions(matchmaking_config_name, fleet_id, gamelift_credentials, timeout,
|
||||
remote_console_instances, multiplayer_client_launcher_list):
|
||||
|
||||
current_launcher_instance = 0
|
||||
|
||||
multiplayer_client_launcher_count = len(multiplayer_client_launcher_list)
|
||||
# Initialized all 4 clients
|
||||
while current_launcher_instance < multiplayer_client_launcher_count:
|
||||
launcher = multiplayer_client_launcher_list[current_launcher_instance]
|
||||
launcher.args = []
|
||||
add_game_lift_creds_and_endpoint_args(launcher, gamelift_credentials)
|
||||
launcher.start()
|
||||
|
||||
# Hard wait for the clients to initialize. Setting to 10s to allow slowest machines to connect
|
||||
time.sleep(10)
|
||||
|
||||
remote_console_instances[current_launcher_instance].start()
|
||||
|
||||
waiter.wait_for(lambda: network_utils.check_for_listening_port(4600 + current_launcher_instance),
|
||||
timeout=timeout,
|
||||
exc=AssertionError('Port 460{} not listening.'.format(current_launcher_instance)))
|
||||
current_launcher_instance += 1
|
||||
|
||||
# 2 clients request matchmaking
|
||||
current_launcher_instance = 0
|
||||
while current_launcher_instance < 2:
|
||||
remote_console_instances[current_launcher_instance].send_command('gamelift_flexmatch {}'
|
||||
.format(matchmaking_config_name))
|
||||
current_launcher_instance += 1
|
||||
|
||||
# for initial delay
|
||||
time.sleep(timeout)
|
||||
|
||||
# Add 1 more client
|
||||
remote_console_instances[current_launcher_instance].send_command('gamelift_flexmatch {}'
|
||||
.format(matchmaking_config_name))
|
||||
# for initial delay
|
||||
time.sleep(timeout)
|
||||
|
||||
current_launcher_instance += 1
|
||||
# Add 1 more client
|
||||
remote_console_instances[current_launcher_instance].send_command('gamelift_flexmatch {}'
|
||||
.format(matchmaking_config_name))
|
||||
|
||||
waiter.wait_for(
|
||||
lambda: gamelift_utils.check_total_players_in_all_game_sessions(fleet_id, multiplayer_client_launcher_count),
|
||||
timeout=timeout, interval=5,
|
||||
exc=AssertionError('Client never connected {}'.format(multiplayer_client_launcher_count)))
|
||||
|
||||
# Disconnect 1 client
|
||||
current_launcher_instance = 0
|
||||
remote_console_instances[current_launcher_instance].send_command('mpdisconnect')
|
||||
waiter.wait_for(
|
||||
lambda: gamelift_utils.check_total_players_in_all_game_sessions(fleet_id, multiplayer_client_launcher_count-1),
|
||||
timeout=timeout, interval=5,
|
||||
exc=AssertionError("Client failed to disconnect"))
|
||||
|
||||
time.sleep(20)
|
||||
|
||||
# Reconnect disconnected client as new player
|
||||
remote_console_instances[current_launcher_instance].send_command('gamelift_flexmatch {}'
|
||||
.format(matchmaking_config_name))
|
||||
waiter.wait_for(
|
||||
lambda: gamelift_utils.check_total_players_in_all_game_sessions(fleet_id, multiplayer_client_launcher_count),
|
||||
timeout=timeout, interval=5,
|
||||
exc=AssertionError("Client never reconnected"))
|
||||
|
||||
# Disconnect 3 clients. Game Session should end.
|
||||
current_launcher_instance = 1
|
||||
while current_launcher_instance < multiplayer_client_launcher_count:
|
||||
remote_console_instances[current_launcher_instance].send_command('mpdisconnect')
|
||||
current_launcher_instance += 1
|
||||
|
||||
waiter.wait_for(
|
||||
lambda: gamelift_utils.check_total_players_in_all_game_sessions(fleet_id, 0),
|
||||
timeout=timeout, interval=5,
|
||||
exc=AssertionError("Client did not disconnect"))
|
||||
|
||||
|
||||
def create_gamelift_game_sessions(fleet_id, queue_name, gamelift_credentials, timeout,
|
||||
remote_console_instances, multiplayer_client_launcher_list):
|
||||
"""
|
||||
Creates Gamelift game sessions. Connects the current clients to the created game sessions.
|
||||
:param fleet_id: Fleet to create game sessions on
|
||||
:param gamelift_credentials: Credentials to use to make GameLift calls
|
||||
:param timeout: Timeout for waiter calls
|
||||
:param remote_console_instances: Remote console instances to work with
|
||||
:param multiplayer_client_launcher_list: Client launcher instances to work with
|
||||
:return:
|
||||
"""
|
||||
|
||||
# First 2 launcher and remote console instances are reserved for creating game sessions
|
||||
current_launcher_instance = 0
|
||||
while current_launcher_instance < 2:
|
||||
launcher = multiplayer_client_launcher_list[current_launcher_instance]
|
||||
launcher.args = []
|
||||
add_game_lift_creds_and_endpoint_args(launcher, gamelift_credentials)
|
||||
launcher.args.extend(['+map', 'gamelobby'])
|
||||
|
||||
if current_launcher_instance == 0:
|
||||
launcher.args.extend(['+gamelift_fleet_id', fleet_id, '+sv_port', '33435'])
|
||||
else:
|
||||
launcher.args.extend(['+gamelift_queue_name', queue_name, '+sv_port', '33436'])
|
||||
|
||||
# Normally we start a launcher using with: which utilizes context managers to ensure teardown
|
||||
# However we are using multiple launchers so we ensure launcher.stop() is called in custom teardown
|
||||
launcher.start()
|
||||
|
||||
# Hard wait for the clients to initialize. Hoping 10secs is enough on the worst machines as well.
|
||||
time.sleep(10)
|
||||
|
||||
# Checks if the port is listening
|
||||
waiter.wait_for(lambda: network_utils.check_for_listening_port(4600 + current_launcher_instance),
|
||||
timeout=timeout,
|
||||
exc=AssertionError('Port 460{} not listening.'.format(current_launcher_instance)))
|
||||
|
||||
# Start the remote console
|
||||
remote_console_instances[current_launcher_instance].start()
|
||||
|
||||
# Set expected log line to assert
|
||||
gamelift_host_event = remote_console_instances[current_launcher_instance].\
|
||||
expect_log_line('(GameLift) - Initialized GameLift client successfully.', 90)
|
||||
|
||||
# Send the remote console command to host a multiplayersample game
|
||||
remote_console_instances[current_launcher_instance].\
|
||||
send_command('gamelift_host AutomationTest multiplayersample 8')
|
||||
|
||||
assert gamelift_host_event(), 'Console never stated gamelift hosting was completed properly'
|
||||
|
||||
current_launcher_instance += 1
|
||||
|
||||
# Assert on if our client connected
|
||||
waiter.wait_for(lambda: gamelift_utils.check_total_players_in_all_game_sessions(fleet_id,
|
||||
current_launcher_instance),
|
||||
timeout=timeout,
|
||||
interval=5,
|
||||
exc=AssertionError("Client {} never connected".format(current_launcher_instance)))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('platform', ['win_x64_vs2017'])
|
||||
@pytest.mark.parametrize('configuration', ['profile'])
|
||||
@pytest.mark.parametrize('project', ['MultiplayerSample'])
|
||||
@pytest.mark.parametrize('spec', ['all'])
|
||||
@pytest.mark.parametrize('level', ['multiplayersample'])
|
||||
@pytest.mark.parametrize('number_of_launchers', [6])
|
||||
class TestRunWindowsGamelift(object):
|
||||
"""
|
||||
This test will upload a build to Gamelift, creates a GameLift fleet and queue.
|
||||
Deploys 2 game sessions one directly on the fleet using fleetId and other using the queue (Queue name).
|
||||
1 game session created using Automatic backfill mode.
|
||||
It then connects clients between these 2 sessions. Half clients are connected using fleetId and
|
||||
other half using queueName
|
||||
Since MultiplayerSample is implemented on the client side to connect to first game session found from the list
|
||||
returned by GameLift describe game sessions and additionally GameLift response is non-deterministic(order),
|
||||
what is being verified is that across 2 game sessions total number of players can be tallied.
|
||||
"""
|
||||
def test_RunWindowsGamelift(self, request, configuration, remote_console_instances,
|
||||
multiplayer_client_launcher_list, workspace):
|
||||
# debug takes longer to load
|
||||
TIMEOUT = 60
|
||||
if configuration == 'debug':
|
||||
TIMEOUT = 120
|
||||
|
||||
# Delete previous logs
|
||||
if os.path.exists(workspace.paths.project_log()):
|
||||
try:
|
||||
shutil.rmtree(workspace.paths.project_log())
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
# Test setup
|
||||
gamelift_credentials = GameliftUtils.get_aws_credentials(AWS_PROFILE)
|
||||
file_utils.clear_out_file(os.path.join(workspace.paths.project(), 'initialmap.cfg'))
|
||||
workspace.configuration = configuration
|
||||
|
||||
# Process Assets
|
||||
process_assets_success = workspace.asset_processor.batch_process()
|
||||
assert process_assets_success, 'Assets did not process correctly'
|
||||
|
||||
# Create PC Gamelift package
|
||||
gamelift_utils.create_gamelift_package(workspace.paths.dev(), workspace.paths._find_bin_dir())
|
||||
|
||||
# Upload Gamelift build
|
||||
build_root = os.path.join(workspace.paths.dev(), 'MultiplayerSample_pc_Paks_Dedicated')
|
||||
gamelift_build_id = GameliftUtils.upload_build_to_gamelift(build_root, 'WindowsGameliftTest', 'WINDOWS_2012',
|
||||
REGION)
|
||||
|
||||
# Assert that the build uploaded successfully
|
||||
waiter.wait_for(lambda: gamelift_utils.is_build_status_ready(gamelift_build_id),
|
||||
timeout=TIMEOUT, interval=INTERVAL_TIME,
|
||||
exc=AssertionError("Gamelift build never became active"))
|
||||
|
||||
# Create a Gamelift fleet
|
||||
runtime_configuration = networking_constants.WINDOWS_GAMELIFT_FLEET['runtime_configuration']
|
||||
ec2_instance_type = networking_constants.WINDOWS_GAMELIFT_FLEET['ec2_instance_type']
|
||||
fleet_name = networking_constants.WINDOWS_GAMELIFT_FLEET['fleet_name']
|
||||
build_dir = "{}.Dedicated".format(workspace.paths._find_bin_dir())
|
||||
for server_process in runtime_configuration['ServerProcesses']:
|
||||
server_process['LaunchPath'] = os.path.join("C:\\", "game", build_dir,
|
||||
"MultiplayerSampleLauncher_Server.exe")
|
||||
# IPs should be manually set when testing locally, or set to clients' IPs when automated.
|
||||
ec2_inbound_permissions = networking_constants.WINDOWS_GAMELIFT_FLEET['ec2_inbound_permissions']
|
||||
fleet_id, fleet_arn = gamelift_utils.create_gamelift_fleet(name=fleet_name,
|
||||
build_id=gamelift_build_id,
|
||||
ec2_instance_type=ec2_instance_type,
|
||||
runtime_configuration=runtime_configuration,
|
||||
ec2_inbound_permissions=ec2_inbound_permissions
|
||||
)
|
||||
|
||||
# Assert that the fleet starts successfully
|
||||
waiter.wait_for(lambda: gamelift_utils.check_instance_state(fleet_id), timeout=FLEET_TIMEOUT,
|
||||
interval=INTERVAL_TIME, exc=AssertionError("Gamelift server never became active"))
|
||||
|
||||
queue_name = 'MSTest-{}'.format(str(uuid.uuid4()))
|
||||
|
||||
# Create GameLift queue
|
||||
queue_arn = gamelift_utils.create_gamelift_queue(queue_name, fleet_arn)
|
||||
|
||||
# Create GameLift matchmaking ruleset
|
||||
rule_set_name = 'MSTest-{}'.format(str(uuid.uuid4()))
|
||||
gamelift_utils.create_gamelift_matchmaking_rule_set(rule_set_name)
|
||||
|
||||
# Create GameLift matchmaking configuration
|
||||
matchmaking_config_name = 'MSTest-{}'.format(str(uuid.uuid4()))
|
||||
gamelift_utils.create_gamelift_matchmaking_config(matchmaking_config_name, queue_arn, rule_set_name,
|
||||
'AUTOMATIC')
|
||||
|
||||
create_gamelift_game_sessions(fleet_id, queue_name, gamelift_credentials, TIMEOUT,
|
||||
remote_console_instances, multiplayer_client_launcher_list)
|
||||
|
||||
# Test clients connecting to game lift servers.
|
||||
create_game_lift_player_sessions(fleet_id, gamelift_credentials, TIMEOUT,
|
||||
remote_console_instances, multiplayer_client_launcher_list)
|
||||
|
||||
# Test flex match creating game session and players connecting to the created flex match game session
|
||||
create_gamelift_flexmatch_game_and_player_sessions(matchmaking_config_name, fleet_id, gamelift_credentials,
|
||||
TIMEOUT, remote_console_instances,
|
||||
multiplayer_client_launcher_list)
|
||||
|
||||
# Verify all clients are alive.
|
||||
for launcher in multiplayer_client_launcher_list:
|
||||
assert launcher.is_alive(), "A process was closed unexpectedly"
|
||||
|
||||
|
||||
@pytest.mark.parametrize('platform', ['win_x64_vs2017'])
|
||||
@pytest.mark.parametrize('configuration', ['profile'])
|
||||
@pytest.mark.parametrize('project', ['MultiplayerSample'])
|
||||
@pytest.mark.parametrize('spec', ['all'])
|
||||
@pytest.mark.parametrize('level', ['multiplayersample'])
|
||||
@pytest.mark.parametrize('number_of_launchers', [4])
|
||||
class TestRunWindowsCustomBackfillGamelift(object):
|
||||
"""
|
||||
This test will upload a build to Gamelift, creates a GameLift fleet and queue. 1 game session is created using
|
||||
custom match backfill.
|
||||
Test players connecting and disconnecting the game session.
|
||||
"""
|
||||
def test_RunWindowsGamelift_Custombackfill(self, request, configuration, remote_console_instances,
|
||||
multiplayer_client_launcher_list, workspace):
|
||||
# debug takes longer to load
|
||||
TIMEOUT = 60
|
||||
if configuration == 'debug':
|
||||
TIMEOUT = 120
|
||||
|
||||
# Test setup
|
||||
gamelift_credentials = GameliftUtils.get_aws_credentials(AWS_PROFILE)
|
||||
file_utils.clear_out_file(os.path.join(workspace.paths.project(), 'initialmap.cfg'))
|
||||
workspace.configuration = configuration
|
||||
|
||||
# Process Assets
|
||||
process_assets_success = workspace.asset_processor.batch_process()
|
||||
assert process_assets_success, 'Assets did not process correctly'
|
||||
|
||||
# Create PC Gamelift package
|
||||
gamelift_utils.create_gamelift_package(workspace.paths.dev(), workspace.paths._find_bin_dir())
|
||||
|
||||
# Upload Gamelift build
|
||||
build_root = os.path.join(workspace.paths.dev(), 'MultiplayerSample_pc_Paks_Dedicated')
|
||||
gamelift_build_id = GameliftUtils.upload_build_to_gamelift(build_root, 'WindowsGameliftTest', 'WINDOWS_2012',
|
||||
REGION)
|
||||
|
||||
# Assert that the build uploaded successfully
|
||||
waiter.wait_for(lambda: gamelift_utils.is_build_status_ready(gamelift_build_id),
|
||||
timeout=TIMEOUT, interval=INTERVAL_TIME,
|
||||
exc=AssertionError("Gamelift build never became active"))
|
||||
|
||||
# Create a Gamelift fleet
|
||||
runtime_configuration = networking_constants.WINDOWS_CUSTOMBACKFILL_GAMELIFT_FLEET['runtime_configuration']
|
||||
ec2_instance_type = networking_constants.WINDOWS_CUSTOMBACKFILL_GAMELIFT_FLEET['ec2_instance_type']
|
||||
fleet_name = networking_constants.WINDOWS_CUSTOMBACKFILL_GAMELIFT_FLEET['fleet_name']
|
||||
build_dir = "{}.Dedicated".format(workspace.paths._find_bin_dir())
|
||||
for server_process in runtime_configuration['ServerProcesses']:
|
||||
server_process['LaunchPath'] = os.path.join("C:\\", "game", build_dir,
|
||||
"MultiplayerSampleLauncher_Server.exe")
|
||||
# IPs should be manually set when testing locally, or set to clients' IPs when automated.
|
||||
ec2_inbound_permissions = networking_constants.WINDOWS_GAMELIFT_FLEET['ec2_inbound_permissions']
|
||||
fleet_id, fleet_arn = gamelift_utils.create_gamelift_fleet(name=fleet_name,
|
||||
build_id=gamelift_build_id,
|
||||
ec2_instance_type=ec2_instance_type,
|
||||
runtime_configuration=runtime_configuration,
|
||||
ec2_inbound_permissions=ec2_inbound_permissions
|
||||
)
|
||||
|
||||
# Assert that the fleet starts successfully
|
||||
waiter.wait_for(lambda: gamelift_utils.check_instance_state(fleet_id), timeout=FLEET_TIMEOUT,
|
||||
interval=INTERVAL_TIME, exc=AssertionError("Gamelift server never became active"))
|
||||
|
||||
queue_name = 'MSTest-{}'.format(str(uuid.uuid4()))
|
||||
|
||||
# Create GameLift queue
|
||||
queue_arn = gamelift_utils.create_gamelift_queue(queue_name, fleet_arn)
|
||||
|
||||
# Create GameLift matchmaking ruleset
|
||||
rule_set_name = 'MSTest-{}'.format(str(uuid.uuid4()))
|
||||
gamelift_utils.create_gamelift_matchmaking_rule_set(rule_set_name)
|
||||
|
||||
# Create GameLift matchmaking configuration
|
||||
matchmaking_config_name = 'MSTest-{}'.format(str(uuid.uuid4()))
|
||||
gamelift_utils.create_gamelift_matchmaking_config(matchmaking_config_name, queue_arn, rule_set_name,
|
||||
'MANUAL')
|
||||
|
||||
# Test flex match creating game session and players connecting to the created custome flex match game session
|
||||
create_gamelift_custom_flexmatch_game_and_player_sessions(matchmaking_config_name, fleet_id, gamelift_credentials,
|
||||
TIMEOUT, remote_console_instances,
|
||||
multiplayer_client_launcher_list)
|
||||
|
||||
# Verify all clients are alive.
|
||||
for launcher in multiplayer_client_launcher_list:
|
||||
assert launcher.is_alive(), "A process was closed unexpectedly"
|
||||
@@ -1,76 +0,0 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import gamelift_utils
|
||||
|
||||
logger = logging.basicConfig(filename='test.log', filemode='w', level=logging.DEBUG)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
This script cleans up the builds and fleets from gamelift. The main method retrieves a list of build and fleet
|
||||
ids, and then sends a console command to delete them. The script will exclude any builds and fleets that are
|
||||
currently in use. It will then validate to make sure all non excluded builds and fleets are deleted.
|
||||
"""
|
||||
exclude_fleet_list = gamelift_utils.get_in_use_fleet_list_from_gamelift()
|
||||
fleet_list = gamelift_utils.get_fleet_list_from_gamelift(exclude_fleet_list)
|
||||
exclude_build_list = gamelift_utils.get_in_use_build_list_from_gamelift()
|
||||
build_list = gamelift_utils.get_build_list_from_gamelift(exclude_build_list)
|
||||
|
||||
for fleet in fleet_list:
|
||||
gamelift_utils.delete_fleet_from_gamelift(fleet)
|
||||
|
||||
assert validate_fleet_list_deleted(exclude_fleet_list), 'Fleet list was not deleted properly!'
|
||||
|
||||
for build in build_list:
|
||||
gamelift_utils.delete_build_from_gamelift(build)
|
||||
|
||||
assert validate_build_list_deleted(exclude_build_list), 'Build list was not deleted properly!'
|
||||
|
||||
|
||||
def validate_build_list_deleted(exclude_build_list):
|
||||
"""
|
||||
Validates whether the builds were deleted correctly
|
||||
:param exclude_build_list: a list of builds that should not be deleted
|
||||
:return: returns True if the builds are deleted correctly
|
||||
"""
|
||||
build_is_empty = True
|
||||
build_list = gamelift_utils.get_build_list_from_gamelift()
|
||||
|
||||
for build in build_list:
|
||||
if build not in exclude_build_list:
|
||||
build_is_empty = False
|
||||
break
|
||||
|
||||
return build_is_empty
|
||||
|
||||
|
||||
def validate_fleet_list_deleted(exclude_fleet_list):
|
||||
"""
|
||||
Validates whether the fleets were deleted correctly
|
||||
:param exclude_fleet_list: a list of fleets that should not be deleted
|
||||
:return: returns True if the fleets are deleted correctly
|
||||
"""
|
||||
fleet_is_empty = True
|
||||
fleet_attribute_list = gamelift_utils.get_fleet_list_attributes_from_gamelift()
|
||||
|
||||
for fleet_attrib in fleet_attribute_list:
|
||||
if (fleet_attrib['Status'] not in ["DELETING"]) and (fleet_attrib['FleetId'] not in exclude_fleet_list):
|
||||
fleet_is_empty = False
|
||||
break
|
||||
|
||||
return fleet_is_empty
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,486 +0,0 @@
|
||||
"""
|
||||
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
import ConfigParser
|
||||
import json
|
||||
import subprocess
|
||||
import shutil
|
||||
from s3_utils import *
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_aws_credentials():
|
||||
"""
|
||||
Gets credentials from .aws/credentials file. QA will setup our nodes using one default set for Isengard
|
||||
:return: Dictionary of AWS Credentials gathered
|
||||
"""
|
||||
aws_credentials = {}
|
||||
home = os.path.expanduser("~")
|
||||
IniRead = ConfigParser.ConfigParser()
|
||||
IniRead.read('{}\.aws\credentials'.format(home))
|
||||
aws_credentials['access'] = IniRead.get('default', 'aws_access_key_id')
|
||||
aws_credentials['secret'] = IniRead.get('default', 'aws_secret_access_key')
|
||||
return aws_credentials
|
||||
|
||||
|
||||
def upload_build_to_gamelift(dev_path):
|
||||
"""
|
||||
Function to upload a build to gamelift
|
||||
:param dev_path: Path to the dev folder of Lumberyard
|
||||
Example command:
|
||||
aws Gamelift upload-build --name "1.11 pc-47 (profile)" --build-version 1.11 --build-root
|
||||
"F:\lumberyard-1.11-472772-pc-47\dev\multiplayersample_pc_paks_dedicated" --operating-system WINDOWS_2012
|
||||
--region us-west-2
|
||||
"""
|
||||
|
||||
upload_build_cmd = ['aws', 'gamelift', 'upload-build', '--name', dev_path, '--build-version', '1.0.0',
|
||||
'--operating-system',
|
||||
'WINDOWS_2012', '--build-root',
|
||||
r'{}\MultiplayerSample_pc_Paks_Dedicated'.format(dev_path),
|
||||
'--region', 'us-west-1']
|
||||
|
||||
proc = subprocess.Popen(upload_build_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=dev_path)
|
||||
lines = []
|
||||
|
||||
for stdout_line in iter(proc.stdout.readline, ''):
|
||||
lines.append(stdout_line)
|
||||
proc.stdout.close()
|
||||
|
||||
assert ('Successfully uploaded' in lines[0]), "Not successfully uploaded"
|
||||
build_id = lines[1].split(" ")[-1:][0].rstrip()
|
||||
|
||||
logger.info(build_id)
|
||||
|
||||
return build_id
|
||||
|
||||
|
||||
def create_gamelift_fleet(dev_path, build_id, build_dir):
|
||||
"""
|
||||
Function to create a fleet within Gamelift
|
||||
:param dev_path: Path to the dev folder of Lumberyard
|
||||
:param build_id: The id of the build given back to us from Gamelift when we uploaded a build
|
||||
:param build_dir: The directory where the build exists
|
||||
:return: ID of the fleet that was created
|
||||
"""
|
||||
upload_build_cmd = ['aws', 'gamelift', 'create-fleet', '--name', dev_path, '--description',
|
||||
'Automated Testing for Networking', '--build-id', build_id,
|
||||
'--ec2-instance-type', 'c3.large', '--runtime-configuration',
|
||||
'ServerProcesses=[{'
|
||||
+ 'LaunchPath=C:\game\{}\MultiplayerSampleLauncher_Server.exe'.format(build_dir) +
|
||||
',Parameters=+sv_port 33435 '
|
||||
'+gamelift_start_server,ConcurrentExecutions=1}]',
|
||||
'--ec2-inbound-permissions', 'FromPort=33435,ToPort=33436,IpRange=0.0.0.0/0,Protocol=UDP',
|
||||
'FromPort=3389,ToPort=3389,IpRange=0.0.0.0/0,Protocol=TCP', '--log-paths', 'c:\game\user\log',
|
||||
'--region', 'us-west-1']
|
||||
|
||||
output, _ = subprocess.Popen(upload_build_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=dev_path).communicate()
|
||||
|
||||
logger.info(output)
|
||||
|
||||
try:
|
||||
fleet_id = json.loads(output)['FleetAttributes']['FleetId']
|
||||
except ValueError:
|
||||
raise AssertionError('Json value not returned when executing upload, build')
|
||||
|
||||
return fleet_id
|
||||
|
||||
|
||||
def check_instance_state(dev_path, fleet_id):
|
||||
"""
|
||||
Checked the state of an instance. Returns true when the instance is in an Active state
|
||||
:param dev_path: Path to the dev folder of Lumberyard
|
||||
:param fleet_id: the Id of the fleet we are checking the state of
|
||||
:return: True when fleet is in the active state
|
||||
"""
|
||||
fleet_attribute = get_fleet_list_attributes_from_gamelift(fleet_id)
|
||||
|
||||
try:
|
||||
if len(fleet_attribute) == 0:
|
||||
return False
|
||||
elif fleet_attribute[0]['Status'] == 'ACTIVE':
|
||||
return True
|
||||
except ValueError:
|
||||
raise AssertionError('Json value not returned when executing upload, build')
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def check_current_player_sessions(fleet_id, number_of_players):
|
||||
"""
|
||||
Checks for the number of players inside a game session
|
||||
:param dev_path: Path to the dev folder of Lumberyard
|
||||
:param fleet_id: The ID of the gamelift fleet
|
||||
:param number_of_players: Number of players to be expecting for this function to verify
|
||||
:return: Returns true of the number of players match the number of connected clients
|
||||
"""
|
||||
game_session = get_game_session_from_gamelift(fleet_id)
|
||||
if len(game_session) == 0:
|
||||
return False
|
||||
elif game_session[0]['CurrentPlayerSessionCount'] == number_of_players:
|
||||
return True
|
||||
|
||||
|
||||
def run_multiplayer_sample_paks_pc_dedicated(dev_path):
|
||||
"""
|
||||
Runs a bat script which will package up the items needed for Gamelift
|
||||
:param dev_path: Path to the dev folder of Lumberyard
|
||||
"""
|
||||
cmd = [dev_path + r'\BuildMultiplayerSample_Paks_PC_dedicated.bat']
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=dev_path)
|
||||
for stdout_line in iter(proc.stdout.readline, ''):
|
||||
logger.info(stdout_line)
|
||||
proc.stdout.close()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'Script to make PC Paks for Gamelift failed'
|
||||
|
||||
|
||||
def copy_bin_folder(bin_directory, dev_path):
|
||||
"""
|
||||
Copies bin folder into the MultiplayerSamples_pc_Paks_Dedicated folder for Windows Gamelift Builds
|
||||
:param bin_directory:
|
||||
:param dev_path:
|
||||
:return: None
|
||||
"""
|
||||
to_folder = dev_path + '\\MultiplayerSample_pc_Paks_Dedicated\\' + bin_directory
|
||||
if os.path.exists(to_folder):
|
||||
shutil.rmtree(to_folder, onerror=onerror)
|
||||
shutil.copytree(os.path.join(dev_path, bin_directory), to_folder)
|
||||
|
||||
|
||||
def download_required_gamelift_files(destination_dir):
|
||||
"""
|
||||
Downloads required vc redistributables and their installation script from S3.
|
||||
:param destination_dir: Path on disk to be checked.
|
||||
"""
|
||||
gamelift_files_bucket = 'ly-net-gamelift-required-files'
|
||||
keys = ['vc_redist.x64.exe', 'VC_redist2017.x64.exe', 'vcredist_x64.exe', 'install.bat',
|
||||
'msvcp140d.dll', 'ucrtbased.dll', 'vcruntime140d.dll']
|
||||
|
||||
for file_key in keys:
|
||||
download_from_bucket(gamelift_files_bucket, file_key, destination_dir)
|
||||
|
||||
|
||||
def create_gamelift_package(dev_path, bin_directory):
|
||||
"""
|
||||
Creates a package ready to be deployed to gamelift via aws cli
|
||||
:param dev_path: Path to dev directory
|
||||
:param bin_directory: path to bin directory
|
||||
:return: None
|
||||
"""
|
||||
run_multiplayer_sample_paks_pc_dedicated(dev_path)
|
||||
|
||||
download_required_gamelift_files(dev_path + r'\MultiplayerSample_pc_Paks_Dedicated')
|
||||
|
||||
copy_bin_folder("{}.Dedicated".format(bin_directory), dev_path)
|
||||
|
||||
|
||||
def onerror(func, path, exc_info):
|
||||
"""
|
||||
Error handler for ``shutil.rmtree``.
|
||||
If the error is due to an access error (read only file)
|
||||
it attempts to add write permission and then retries.
|
||||
If the error is for another reason it re-raises the error.
|
||||
"""
|
||||
import stat
|
||||
if not os.access(path, os.W_OK):
|
||||
# Is the error an access error ?
|
||||
os.chmod(path, stat.S_IWUSR)
|
||||
func(path)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def get_game_session_from_gamelift(fleet_id):
|
||||
get_game_session_cmd = ['aws', 'gamelift', 'describe-game-sessions', '--fleet-id', fleet_id]
|
||||
try:
|
||||
output, _ = subprocess.Popen(get_game_session_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_game_session_from_gamelift command failed'
|
||||
|
||||
logger.info(output)
|
||||
|
||||
try:
|
||||
game_session = json.loads(output)['GameSessions']
|
||||
except ValueError:
|
||||
raise AssertionError('Json values not returned when executing get_game_session_cmd command')
|
||||
# Return None if there is no active game_session for the fleet
|
||||
|
||||
return game_session
|
||||
|
||||
|
||||
def get_build_id_from_fleet_id(fleet_id):
|
||||
get_build_from_id_cmd = ['aws', 'gamelift', 'describe-fleet-attributes', '--fleet-id', fleet_id]
|
||||
try:
|
||||
output, _ = subprocess.Popen(get_build_from_id_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_build_id_from_fleet_id command failed'
|
||||
|
||||
logger.info(output)
|
||||
|
||||
try:
|
||||
fleet_attribute = json.loads(output)['FleetAttributes']
|
||||
build_id = fleet_attribute[0]['BuildId']
|
||||
except ValueError:
|
||||
raise AssertionError('Json values not returned when executing get_build_id_from_fleet_id command')
|
||||
|
||||
return build_id
|
||||
|
||||
|
||||
def get_exclude_build_list_from_gamelift():
|
||||
"""
|
||||
Function to get the excluded builds and return them to a new list. Excluded builds include those that are being
|
||||
initalized and if an excluded fleet is using that build
|
||||
:return: list
|
||||
"""
|
||||
exclude_build_list = []
|
||||
|
||||
exclude_fleet_list = get_exclude_fleet_list_from_gamelift()
|
||||
for fleet in exclude_fleet_list:
|
||||
exclude_build_list.append(get_build_id_from_fleet_id(fleet))
|
||||
|
||||
build_list = get_build_list_from_gamelift()
|
||||
|
||||
for build_id in build_list:
|
||||
get_describe_build_list_cmd = ['aws', 'gamelift', 'describe-build', '--build-id', build_id]
|
||||
|
||||
try:
|
||||
output, error = subprocess.Popen(get_describe_build_list_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, ).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_describe_build_list_cmd command failed'
|
||||
|
||||
try:
|
||||
build = json.loads(output)['Build']
|
||||
if build['Status'] == 'INITIALIZED':
|
||||
exclude_build_list.append(build['BuildId'])
|
||||
except ValueError:
|
||||
raise AssertionError('Json values not returned when executing get_describe_build_list_cmd command')
|
||||
|
||||
return exclude_build_list
|
||||
|
||||
|
||||
def get_exclude_fleet_list_from_gamelift():
|
||||
"""
|
||||
Function to remove the excluded fleets and return them to a new list. Excluded fleets include those that are being
|
||||
initialized or currently in use in an active game session.
|
||||
:return: list
|
||||
"""
|
||||
exclude_fleet_list = []
|
||||
fleet_list = get_fleet_list_from_gamelift()
|
||||
|
||||
fleet_attribute_list = get_fleet_list_attributes_from_gamelift()
|
||||
for fleet_attrib in fleet_attribute_list:
|
||||
if fleet_attrib['Status'] in ["DOWNLOADING", "ACTIVATING", "NEW", "VALIDATING", "BUILDING"]:
|
||||
exclude_fleet_list.append(fleet_attrib['FleetId'])
|
||||
|
||||
for fleet_id in fleet_list:
|
||||
game_session = get_game_session_from_gamelift(fleet_id)
|
||||
if len(game_session) != 0 and game_session[0]['CurrentPlayerSessionCount'] > 0:
|
||||
exclude_fleet_list.append(fleet_id)
|
||||
|
||||
return exclude_fleet_list
|
||||
|
||||
|
||||
def get_fleet_list_from_gamelift(exclude_fleet_list=[]):
|
||||
"""
|
||||
Function to retrieve all fleet Id's from gamelift that are not in the excluded list. If no list is provided, it will
|
||||
return all fleet Id's
|
||||
:return: list
|
||||
"""
|
||||
get_fleet_list_cmd = ['aws', 'gamelift', 'list-fleets']
|
||||
|
||||
try:
|
||||
output, error = subprocess.Popen(get_fleet_list_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_fleet_list_cmd command failed'
|
||||
|
||||
logger.info(output)
|
||||
next_token = None
|
||||
|
||||
try:
|
||||
next_token = json.loads(output)['NextToken']
|
||||
except KeyError:
|
||||
pass
|
||||
# If the NextToken exists take it, otherwise ignore that we don't have one
|
||||
|
||||
try:
|
||||
all_fleet_list = json.loads(output)['FleetIds']
|
||||
if next_token is not None:
|
||||
get_next_fleet_list_cmd = ['aws', 'gamelift', 'list-fleets', '--next-token', next_token]
|
||||
try:
|
||||
output, error = subprocess.Popen(get_next_fleet_list_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, ).communicate()
|
||||
next_fleet_list = json.loads(output)['FleetIds']
|
||||
all_fleet_list += next_fleet_list
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_next_fleet_list_cmd command failed'
|
||||
|
||||
logger.info(output)
|
||||
|
||||
except ValueError:
|
||||
raise AssertionError('Json values not returned when executing get_fleet_list_cmd command')
|
||||
|
||||
fleet_list = []
|
||||
for fleet in all_fleet_list:
|
||||
if fleet not in exclude_fleet_list:
|
||||
fleet_list.append(fleet)
|
||||
return fleet_list
|
||||
|
||||
|
||||
def get_build_list_from_gamelift(exclude_build_list=[]):
|
||||
"""
|
||||
Function to retrieve all build ID's from gamelift that are not in the excluded list. If no list is provided, it will
|
||||
return all build Id's
|
||||
:return: list
|
||||
"""
|
||||
get_build_list_cmd = ['aws', 'gamelift', 'list-builds']
|
||||
|
||||
try:
|
||||
output, _ = subprocess.Popen(get_build_list_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_build_list_cmd command failed'
|
||||
|
||||
logger.info(output)
|
||||
|
||||
try:
|
||||
build_list = json.loads(output)['Builds']
|
||||
except ValueError:
|
||||
raise AssertionError('Json values not returned when executing get_build_list_cmd command')
|
||||
|
||||
build_id_list = []
|
||||
for build in build_list:
|
||||
if build['BuildId'] not in exclude_build_list:
|
||||
build_id_list.append(build['BuildId'])
|
||||
return build_id_list
|
||||
|
||||
|
||||
def get_fleet_list_attributes_from_gamelift(fleet_id=None):
|
||||
"""
|
||||
Function to retrieve the fleet attribute for a given fleet. Will return all fleet attributes if no fleet_id is given
|
||||
:return: list
|
||||
"""
|
||||
if fleet_id is None:
|
||||
get_fleet_list_attributes_cmd = ['aws', 'gamelift', 'describe-fleet-attributes']
|
||||
else:
|
||||
get_fleet_list_attributes_cmd = ['aws', 'gamelift', 'describe-fleet-attributes', '--fleet-id', fleet_id]
|
||||
try:
|
||||
output, _ = subprocess.Popen(get_fleet_list_attributes_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_fleet_list_attributes_cmd command failed'
|
||||
|
||||
logger.info(output)
|
||||
next_token = None
|
||||
|
||||
try:
|
||||
next_token = json.loads(output)['NextToken']
|
||||
except KeyError:
|
||||
pass
|
||||
# If the NextToken exists take it, otherwise ignore that we don't have one
|
||||
|
||||
try:
|
||||
fleet_attribute_list = json.loads(output)['FleetAttributes']
|
||||
if next_token is not None:
|
||||
get_next_fleet_list_cmd = ['aws', 'gamelift', 'describe-fleet-attributes', '--next-token', next_token]
|
||||
try:
|
||||
output, error = subprocess.Popen(get_next_fleet_list_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, ).communicate()
|
||||
next_fleet_list = json.loads(output)['FleetAttributes']
|
||||
fleet_attribute_list += next_fleet_list
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift get_next_fleet_list_cmd next token command failed'
|
||||
|
||||
logger.info(output)
|
||||
|
||||
except ValueError:
|
||||
raise AssertionError('Json values not returned when executing get_fleet_list_attributes_cmd command')
|
||||
|
||||
return fleet_attribute_list
|
||||
|
||||
|
||||
def delete_build_from_gamelift(build_id):
|
||||
"""
|
||||
Function to delete a build from gamelift
|
||||
:param build_id: The id of the build given back to us from Gamelift when we uploaded a build
|
||||
Example command:
|
||||
aws Gamelift delete-build --build-id build-f4t3gFDS32dfDa
|
||||
"""
|
||||
delete_build_cmd = ['aws', 'gamelift', 'delete-build', '--build-id', build_id]
|
||||
try:
|
||||
output, _ = subprocess.Popen(delete_build_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift delete_build_cmd command failed'
|
||||
|
||||
logger.info(output)
|
||||
|
||||
|
||||
def delete_fleet_from_gamelift(fleet_id):
|
||||
"""
|
||||
Function to delete a fleet from gamelift
|
||||
:param fleet_id: The id of the fleet given back to us from Gamelift when we created a fleet
|
||||
Example command:
|
||||
aws Gamelift delete-build --build-id build-f4t3gFDS32dfDa
|
||||
"""
|
||||
|
||||
# Update fleet capacity command must be ran before deleting the fleet
|
||||
update_fleet_capacity_cmd = ['aws', 'gamelift', 'update-fleet-capacity', '--fleet-id', fleet_id, '--desired-instances', '0']
|
||||
|
||||
try:
|
||||
output_update, _ = subprocess.Popen(update_fleet_capacity_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift update_fleet_capacity_cmd command failed'
|
||||
|
||||
logger.info(output_update)
|
||||
|
||||
delete_fleet_cmd = ['aws', 'gamelift', 'delete-fleet', '--fleet-id', fleet_id]
|
||||
|
||||
try:
|
||||
output_delete, _ = subprocess.Popen(delete_fleet_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT).communicate()
|
||||
except subprocess.CalledProcessError as e:
|
||||
assert e.returncode > 0, 'AWS Gamelift delete_fleet_cmd command failed'
|
||||
|
||||
logger.info(output_delete)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user