ebcli/operations/buildspecops.py (117 lines of code) (raw):
# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import time
from datetime import timedelta
from cement.utils.misc import minimal_logger
from ebcli.core import io
from ebcli.lib import elasticbeanstalk, codebuild, utils
from ebcli.objects.exceptions import ServiceError, ValidationError
from ebcli.resources.strings import strings
LOG = minimal_logger(__name__)
def stream_build_configuration_app_version_creation(app_name, app_version_label, build_spec):
# Get the CloudWatch logs link
successfully_generated = wait_for_app_version_attribute(
app_name,
[app_version_label],
timeout=1
)
app_version_response = elasticbeanstalk.get_application_versions(
app_name,
version_labels=[app_version_label]
)['ApplicationVersions']
build_response = codebuild.batch_get_builds([app_version_response[0]['BuildArn']]) \
if successfully_generated else None
codebuild_timeout = build_spec.timeout or 60
if build_response is not None and 'logs' in build_response['builds'][0]:
log_link_text = strings['codebuild.buildlogs'].replace(
'{logs_link}',
build_response['builds'][0]['logs']['deepLink']
)
io.echo(log_link_text)
io.echo(
"NOTE: The CodeBuild timeout is set to {0} minutes, so this "
"operation may take upto '{0}' minutes to complete.".format(codebuild_timeout)
)
else:
io.log_warning("Could not retrieve CloudWatch link for CodeBuild logs")
try:
# Need to lazy-import `ebcli.lib.commonops` because `pytest` is unable to load it
# at module load-time using Python 2.7 and Python 3.4
from ebcli.operations import commonops
timeout_error_message = ' '.join([
'The CodeBuild build timed out after {} minute(s).'.format(codebuild_timeout),
"To increase the time limit, use the 'Timeout' option in the 'buildspec.yml' file."
])
commonops.wait_for_success_events(
app_name=app_name,
can_abort=False,
request_id=None,
timeout_error_message=timeout_error_message,
timeout_in_minutes=codebuild_timeout,
version_label=app_version_label
)
except ServiceError as exception:
LOG.debug("Caught service error while creating application version '{0}' "
"deleting the created application version as it is useless now.".format(app_version_label))
elasticbeanstalk.delete_application_version(app_name, app_version_label)
raise exception
def validate_build_config(build_config):
if build_config.service_role is not None:
from ebcli.lib.iam import get_roles
role = build_config.service_role
validated_role = None
existing_roles = get_roles()
for existing_role in existing_roles:
if role == existing_role['Arn'] or role == existing_role['RoleName']:
validated_role = existing_role['Arn']
break
if validated_role is None:
LOG.debug("Role '{0}' not found in retrieved list of roles".format(role))
raise ValidationError("Role '{0}' does not exist.".format(role))
build_config.service_role = validated_role
else:
io.log_warning(
"To learn more about creating a service role for CodeBuild, see Docs:"
" https://docs-aws.amazon.com/codebuild/latest/userguide/"
"setting-up.html#setting-up-service-role"
)
raise ValidationError("No service role specified in buildspec; this is a required argument.")
if build_config.image is None:
raise ValidationError("No image specified in buildspec; this is a required argument.")
def wait_for_app_version_attribute(app_name, version_labels, timeout=5):
io.echo('--- Waiting for Application Versions to populate attributes ---')
versions_to_check = list(version_labels)
found = dict.fromkeys(version_labels)
failed = dict.fromkeys(version_labels)
start_time = utils.datetime_utcnow()
timediff = timedelta(minutes=timeout)
while versions_to_check:
if _timeout_reached(start_time, timediff):
io.log_error(
strings['appversion.attribute.failed'].replace(
'{app_version}',
', '.join(version_labels)
)
)
return False
io.LOG.debug('Retrieving app versions.')
app_versions = elasticbeanstalk.get_application_versions(
app_name,
versions_to_check
)['ApplicationVersions']
for version in app_versions:
if version.get('BuildArn'):
found[version['VersionLabel']] = True
io.echo(strings['appversion.attribute.success'].format(app_version=version['VersionLabel']))
versions_to_check.remove(version['VersionLabel'])
elif version.get('Status') == 'FAILED':
failed[version['VersionLabel']] = True
io.log_error(
strings['appversion.attribute.failed'].format(
app_version=version['VersionLabel']
)
)
versions_to_check.remove(version['VersionLabel'])
if all(found.values()):
return True
_sleep()
return not any(failed.values())
def _sleep():
time.sleep(4)
def _timeout_reached(start_time, timediff):
return utils.datetime_utcnow() - start_time >= timediff