예제 #1
0
def remove_entry(context, file_path, version_id=None):
    dynamoDB = context.aws.client(
        'dynamodb',
        region=resource_manager.util.get_region_from_arn(
            context.config.project_stack_id))
    versioned = content_bucket.content_versioning_enabled(
        context, context.config.default_deployment)
    table_arn = get_staging_table(context, context.config.default_deployment,
                                  versioned)
    key = {
        'FileName': {
            'S': file_path
        },
        'VersionId': {
            'S': version_id
        }
    } if versioned else {
        'FileName': {
            'S': file_path
        }
    }
    try:
        response = dynamoDB.delete_item(TableName=table_arn, Key=key)
    except Exception as e:
        raise HandledError(
            'Could not delete entry for for'.format(FilePath=file_path, ), e)
예제 #2
0
def get_children_by_parent_name_and_version(
        context: object,
        parent_name: str,
        parent_version_id: str = None) -> list:
    dynamoDB = context.aws.client(
        'dynamodb',
        region=resource_manager.util.get_region_from_arn(
            context.config.project_stack_id))
    versioned = content_bucket.content_versioning_enabled(
        context, context.config.default_deployment)
    table_arn = get_staging_table(context, context.config.default_deployment,
                                  versioned)

    response = dynamoDB.scan(
        TableName=table_arn,
        ExpressionAttributeNames={"#Parent": "Parent"},
        ExpressionAttributeValues={':parentnameval': {
            'S': parent_name
        }},
        FilterExpression='#Parent = :parentnameval')
    children_by_parent_name = response.get('Items', [])
    children_by_parent_name_and_version = []
    for child in children_by_parent_name:
        if parent_version_id and (parent_version_id not in child.get(
                'ParentVersionIds', {}).get('SS', [])):
            continue
        children_by_parent_name_and_version.append(child)
    return children_by_parent_name_and_version
예제 #3
0
def command_show_uploaded(context, args) -> None:
    """
    List all uploaded content in the DLC bucket
    """
    versioned = content_bucket.content_versioning_enabled(
        context, context.config.default_deployment)
    if versioned:
        raise RuntimeError(
            "[WARNING] Versioning is not supported for this command. Please use list-file-versions and list-bucket-content"
        )
    _service_call_list_content(context, args)
def export_current_table_entries(context: object, deployment_name: str):
    """
        Export existing table entries to a local backup file

        Arguments
        context -- context to use
        deployment_name -- name of the deployment

    """
    try:
        versioned = content_bucket.content_versioning_enabled(
            context, deployment_name)
    except Exception as e:
        if str(e) == 'No stack_id provided.':
            return
        raise e

    current_table = staging.get_staging_table(context, deployment_name,
                                              versioned)
    if not current_table:
        return

    client = context.aws.client('dynamodb')
    try:
        # Scan all the data in the current table
        response = client.scan(TableName=current_table)
        entries = response.get('Items', [])

        while 'LastEvaluatedKey' in response:
            response = client.scan(
                TableName=current_table,
                ExclusiveStartKey=response['LastEvaluatedKey'])
            entries += response.get('Items', [])
    except Exception as e:
        print(f'Failed to scan current table entries. Error: {e}')
        return

    num_entries = len(entries)
    if not num_entries:
        print('No table entry was found.')
        return
    print(f'Found {num_entries} table entries.')

    # Save the backup data to local disk
    backup_folder = dynamic_content_settings.get_table_backup_folder(
        context.config.game_directory_path)
    if not os.path.exists(backup_folder):
        os.makedirs(backup_folder)
    backup_path = os.path.join(backup_folder,
                               deployment_name + BACKUP_FILE_SUFFIX)
    with open(backup_path, 'w+') as backup:
        backup_content = {'versioned': versioned, 'entries': entries}
        json.dump(backup_content, backup)
예제 #5
0
def empty_staging_table(context, deleted_object_list):
    dynamoDB = context.aws.client(
        'dynamodb',
        region=resource_manager.util.get_region_from_arn(
            context.config.project_stack_id))
    versioned = content_bucket.content_versioning_enabled(
        context, context.config.default_deployment)
    table_arn = get_staging_table(context, context.config.default_deployment,
                                  versioned)
    for this_item in deleted_object_list:
        key_path = this_item.get('Key')
        if versioned:
            remove_entry(context, key_path, this_item.get('VersionId'))
        else:
            remove_entry(context, key_path)
def command_suspend_versioning(context: object, args: dict):
    """
        Suspend dynamic content versioning

        Arguments
        context -- context to use
        args -- arguments from the CLI command

    """
    deployment_name = args.deployment_name if args.deployment_name else context.config.default_deployment
    if not content_bucket.content_versioning_enabled(context, deployment_name):
        print('Content versioning is not enabled')
        return

    message = 'Suspend versioning to stop accruing new versions of the same object in the content bucket. '\
        'Existing objects in the content bucket do not change but the staging settings for any noncurrent version will be lost. '\
        'Update your deployment after running this command for the suspension to take effect.'
    if not args.confirm_versioning_suspension:
        context.view.confirm_message(message)

    delete_tags(context, deployment_name, ['content-versioning'])
예제 #7
0
def command_delete_uploaded(context, args) -> None:
    """
    Deletes a staged manifest or PAK (plus any children)
    """
    if '.json' in args.file_path:
        print(f"Removing {args.file_path}")
    else:
        print(f"Removing {args.file_path} and any children")
    versioned = content_bucket.content_versioning_enabled(
        context, context.config.default_deployment)

    if versioned:
        raise RuntimeError(
            "[WARNING] Versioning is not supported for this command. Please use clear-dynamic-content"
        )
    else:
        files = [args.file_path]
        children = staging.get_children_by_parent_name_and_version(
            context, args.file_path, args.version_id)
        for child in children:
            files.append(child.get('FileName', {}).get('S'))
        _service_call_delete_file(context, args, files=files)
예제 #8
0
def signing_status_changed(context, key, do_signing, version_id):
    dynamoDB = context.aws.client(
        'dynamodb',
        region=resource_manager.util.get_region_from_arn(
            context.config.project_stack_id))
    versioned = content_bucket.content_versioning_enabled(
        context, context.config.default_deployment)
    table_arn = get_staging_table(context, context.config.default_deployment,
                                  versioned)
    key = {
        'FileName': {
            'S': key
        }
    } if not versioned else {
        'FileName': {
            'S': key
        },
        'VersionId': {
            'S': version_id
        }
    }

    try:
        response = dynamoDB.get_item(TableName=table_arn, Key=key)

    except ClientError as ce:
        if ce.response['Error']['Code'] == 'ResourceNotFoundException':
            return True
        else:
            raise HandledError(
                'Could not get signing status for {}'.format(key), ce)
    except Exception as e:
        raise HandledError('Failed to get signing status for {}'.format(key),
                           e)

    pak_is_signed = response.get('Item', {}).get('Signature', {}).get('S',
                                                                      '') != ''
    return pak_is_signed != do_signing
def import_table_entries_from_backup(context: object, deployment_name: str):
    """
        Load table entries from a local backup file and import them to the current table

        Arguments
        context -- context to use
        deployment_name -- name of the deployment

    """
    backup_folder = dynamic_content_settings.get_table_backup_folder(
        context.config.game_directory_path)
    backup_path = os.path.join(backup_folder,
                               deployment_name + BACKUP_FILE_SUFFIX)
    if not os.path.exists(backup_path):
        print(f'Backup file {backup_path} does not exist. No data to migrate')
        return
    with open(backup_path, 'r') as backup:
        # Load backup data from local disk
        backup_content = json.load(backup)

    versioned = content_bucket.content_versioning_enabled(
        context, deployment_name)
    if (backup_content['versioned'] == versioned):
        # Version status doesn't change before and after the deployment update
        # No need to migrate any data in this case
        __remove_backup_file(backup_path)
        return

    entries = backup_content['entries']

    current_table = staging.get_staging_table(context, deployment_name,
                                              versioned)
    client = context.aws.client('dynamodb')
    entries_with_failure = []
    for entry in entries:
        if versioned:
            entry['VersionId'] = {
                'S':
                content_bucket.get_latest_version_id(context,
                                                     entry['FileName']['S'],
                                                     deployment_name)
            }
            if entry.get('Parent', {}).get('S'):
                entry['ParentVersionIds'] = {
                    'SS': [
                        content_bucket.get_latest_version_id(
                            context, entry['Parent']['S'], deployment_name)
                    ]
                }
        else:
            if not content_bucket.is_latest_version(
                    context, entry['FileName']['S'], entry['VersionId']['S'],
                    deployment_name):
                # After content versioning is suspended, we should only migrate the staging status
                # for the latest versions of files
                continue
            entry.pop('VersionId', None)
            entry.pop('ParentVersionIds', None)

        try:
            # Check whether the key already exists in the current table
            key = {
                'FileName': entry['FileName'],
                'VersionId': entry['VersionId']
            } if versioned else {
                'FileName': entry['FileName']
            }
            existing_item = client.get_item(TableName=current_table,
                                            Key=key).get('Item', {})
        except Exception as e:
            print(f'Failed to get table entry with key {key}. Error: {e}')
            entries_with_failure.append(entry)
            continue
        if existing_item:
            print(f'Table entry with key {key} already exists.')
            entries_with_failure.append(entry)
            continue

        try:
            # Migrate the entry to the current table
            client.put_item(TableName=current_table, Item=entry)
            print(f'Migrated table entry with key {key}.')
        except Exception as e:
            print(f'Failed to migrate table entry with key {key}. Error: {e}')
            entries_with_failure.append(entry)

    if len(entries_with_failure):
        with open(backup_path, 'w+') as backup:
            json.dump(entries_with_failure, backup)
        print(
            'No all entries were migrated successfully. Please retry the migration with the migrate-unversioned-data command.'
        )
    else:
        __remove_backup_file(backup_path)
예제 #10
0
def set_staging_status(file_path,
                       context,
                       staging_args,
                       deployment_name,
                       version_id=None):
    staging_status = staging_args.get('StagingStatus')
    staging_start = staging_args.get('StagingStart')
    staging_end = staging_args.get('StagingEnd')
    signature = staging_args.get('Signature')
    parent_pak = staging_args.get('Parent')
    parent_version_id = staging_args.get('ParentVersionId')
    file_size = staging_args.get('Size')
    file_hash = staging_args.get('Hash')

    if staging_status == 'WINDOW':
        if not staging_start:
            raise HandledError('No start-date set for WINDOW staging_status')

        if not staging_end:
            raise HandledError('No end-date set for WINDOW staging_status')

    dynamoDB = context.aws.client(
        'dynamodb',
        region=resource_manager.util.get_region_from_arn(
            context.config.project_stack_id))
    versioned = content_bucket.content_versioning_enabled(
        context, deployment_name)
    table_arn = get_staging_table(context, deployment_name, versioned)
    key = {
        'FileName': {
            'S': file_path
        }
    } if not versioned else {
        'FileName': {
            'S': file_path
        },
        'VersionId': {
            'S': version_id
        }
    }
    attributeUpdate = {}

    if staging_status:
        attributeUpdate['StagingStatus'] = {'Value': {'S': staging_status}}

    if parent_pak and len(parent_pak):
        attributeUpdate['Parent'] = {'Value': {'S': parent_pak}}

    if versioned and parent_version_id:
        response = dynamoDB.get_item(TableName=table_arn, Key=key)
        item = response.get('Item', {})
        parent_version_ids = item.get('ParentVersionIds', {}).get('SS', [])
        if parent_version_id not in parent_version_ids:
            parent_version_ids.append(parent_version_id)
            attributeUpdate['ParentVersionIds'] = {
                'Value': {
                    'SS': parent_version_ids
                }
            }

    if signature:
        # Ensure signature from UX is handled
        if isinstance(signature, (bytes, bytearray)):
            signature = signature.decode('utf-8')
        attributeUpdate['Signature'] = {'Value': {'S': signature}}
    else:
        attributeUpdate['Signature'] = {'Action': 'DELETE'}

    if file_size is not None:
        attributeUpdate['Size'] = {'Value': {'S': str(file_size)}}

    if file_hash is not None and len(file_hash) > 0:
        attributeUpdate['Hash'] = {'Value': {'S': str(file_hash)}}

    if staging_status == 'WINDOW':
        time_start = None
        if staging_start.lower() == 'now':
            time_start = get_formatted_time_string(datetime.datetime.utcnow())
        else:
            # Validate our input
            time_start = get_formatted_time_string(
                get_struct_time(staging_start))

        time_end = None
        if staging_end.lower() == 'never':
            time_end = get_formatted_time_string(
                datetime.datetime.utcnow() + datetime.timedelta(
                    days=dynamic_content_settings.get_max_days()))
        else:
            # Validate our input
            time_end = get_formatted_time_string(get_struct_time(staging_end))

        attributeUpdate['StagingStart'] = {'Value': {'S': time_start}}

        attributeUpdate['StagingEnd'] = {'Value': {'S': time_end}}

    try:
        response = dynamoDB.update_item(TableName=table_arn,
                                        Key=key,
                                        AttributeUpdates=attributeUpdate,
                                        ReturnValues='ALL_NEW')
    except Exception as e:
        raise HandledError(
            'Could not update status for'.format(FilePath=file_path, ), e)
    item_data = response.get('Attributes', None)

    if not staging_status:
        # Staging status doesn't change
        return

    new_staging_status = {
        'StagingStatus': item_data.get('StagingStatus', {}).get('S', 'UNSET'),
        'StagingStart': item_data.get('StagingStart', {}).get('S'),
        'StagingEnd': item_data.get('StagingEnd', {}).get('S'),
        'Parent': item_data.get('Parent', {}).get('S')
    }

    show_manifest.show_staging_status(file_path, new_staging_status)