示例#1
0
    def test_get_build_spec_info(self, mock_yaml_load, mock_codecs):
        image = 'aws/codebuild/eb-java-8-amazonlinux-64:2.1.3'
        compute_type = 'BUILD_GENERAL1_SMALL'
        service_role = 'eb-test'
        timeout = 60

        mock_yaml_load.return_value = {
            fileoperations.buildspec_config_header: {
                'ComputeType': compute_type,
                'CodeBuildServiceRole': service_role,
                'Image': image,
                'Timeout': timeout
            }
        }

        expected_build_config = BuildConfiguration(image=image,
                                                   compute_type=compute_type,
                                                   service_role=service_role,
                                                   timeout=timeout)
        actual_build_config = fileoperations.get_build_configuration()
        self.assertEqual(
            expected_build_config.__str__(), actual_build_config.__str__(),
            "Expected '{0}' but got: {1}".format(
                expected_build_config.__str__(),
                actual_build_config.__str__()))
    def test_make_new_env_from_code_in_directory__non_interactive_mode__buildspec_present__using_application_code_in_directory_to_create_app_version(
            self, download_and_extract_sample_app_mock,
            stream_build_configuration_app_version_creation_mock,
            create_app_version_mock, upload_keypair_if_needed_mock,
            wait_for_success_events_mock, get_current_branch_environment_mock,
            create_env_mock, log_info_mock, git_management_enabled_mock,
            build_spec_exists_mock, get_build_configuration_mock,
            resolve_roles_mock):
        resolve_roles_mock.side_effect = None
        build_spec_exists_mock.return_value = True
        build_config = BuildConfiguration(
            compute_type='t2.micro',
            image='java-ami',
            service_role='codebuild-service-role',
            timeout=10)
        get_build_configuration_mock.return_value = build_config
        git_management_enabled_mock.return_value = False
        create_environment_result_mock = mock.MagicMock()
        create_environment_result_mock.name = 'result'
        create_env_mock.return_value = (create_environment_result_mock,
                                        'request-id')
        get_current_branch_environment_mock.return_value = 'environment-1'
        env_request = CreateEnvironmentRequest(
            app_name='my-application',
            env_name='environment-1',
            cname='cname-1',
            platform=SolutionStack(
                '64bit Amazon Linux 2015.09 v2.0.6 running Multi-container Docker 1.7.1 (Generic)'
            ),
            elb_type='network',
            group_name='dev',
            key_name='aws-eb-us-west-2')
        create_app_version_mock.return_value = 'version-label-1'

        createops.make_new_env(env_request, interactive=True)

        stream_build_configuration_app_version_creation_mock.assert_called_once_with(
            'my-application', 'version-label-1', build_config)
        create_environment_result_mock.print_env_details.assert_called_once_with(
            createops.io.echo,
            createops.elasticbeanstalk.get_environments,
            createops.elasticbeanstalk.get_environment_resources,
            health=False)
        wait_for_success_events_mock.assert_called_once_with(
            'request-id', timeout_in_minutes=None)
        create_env_mock.assert_called_once_with(env_request, interactive=True)
        upload_keypair_if_needed_mock.assert_called_once_with(
            'aws-eb-us-west-2')
        create_app_version_mock.assert_called_once_with(
            'my-application', build_config=build_config, process=False)
        log_info_mock.assert_has_calls([
            mock.call('Creating new application version using project code'),
            mock.call('Creating new environment')
        ])
        download_and_extract_sample_app_mock.assert_not_called()
示例#3
0
def get_build_configuration():
    # Values expected in the eb config section in BuildSpec
    service_role_key = 'CodeBuildServiceRole'
    image_key = 'Image'
    compute_key = 'ComputeType'
    timeout_key = 'Timeout'

    # get setting from global if it exists
    cwd = os.getcwd()  # save working directory

    try:
        _traverse_to_project_root()

        build_spec = _get_yaml_dict(buildspec_name)

        # Assert that special beanstalk section exists
        if build_spec is None or buildspec_config_header not in build_spec.keys(
        ):
            LOG.debug("Buildspec Keys: {0}".format(build_spec.keys()))
            io.log_warning(strings['codebuild.noheader'].replace(
                '{header}', buildspec_config_header))
            return None

        beanstalk_build_configs = build_spec[buildspec_config_header]

        if beanstalk_build_configs is None:
            LOG.debug("No values for EB header in buildspec file")
            return BuildConfiguration()

        LOG.debug("EB Config Keys: {0}".format(beanstalk_build_configs.keys()))

        build_configuration = BuildConfiguration(
            compute_type=beanstalk_build_configs.get(compute_key),
            image=beanstalk_build_configs.get(image_key),
            service_role=beanstalk_build_configs.get(service_role_key),
            timeout=beanstalk_build_configs.get(timeout_key))
    finally:
        os.chdir(cwd)  # move back to working directory

    return build_configuration
示例#4
0
def get_build_configuration():
    service_role_key = 'CodeBuildServiceRole'
    image_key = 'Image'
    compute_key = 'ComputeType'
    timeout_key = 'Timeout'

    cwd = os.getcwd()

    try:
        ProjectRoot.traverse()

        build_spec = _get_yaml_dict(buildspec_name)

        if build_spec is None or buildspec_config_header not in build_spec.keys(
        ):
            LOG.debug("Buildspec Keys: {0}".format(build_spec.keys()))
            io.log_warning(strings['codebuild.noheader'].replace(
                '{header}', buildspec_config_header))
            return None

        beanstalk_build_configs = build_spec[buildspec_config_header]

        if beanstalk_build_configs is None:
            LOG.debug("No values for EB header in buildspec file")
            return BuildConfiguration()

        LOG.debug("EB Config Keys: {0}".format(beanstalk_build_configs.keys()))

        build_configuration = BuildConfiguration(
            compute_type=beanstalk_build_configs.get(compute_key),
            image=beanstalk_build_configs.get(image_key),
            service_role=beanstalk_build_configs.get(service_role_key),
            timeout=beanstalk_build_configs.get(timeout_key))
    finally:
        os.chdir(cwd)

    return build_configuration
示例#5
0
def get_build_configuration():
    # TODO: Verify this is correct.
    # Values expected in the eb config section in BuildSpec
    service_role_key = 'CodeBuildServiceRole'
    image_key = 'Image'
    compute_key = 'ComputeType'
    timeout_key = 'Timeout'

    # get setting from global if it exists
    cwd = os.getcwd()  # save working directory

    try:
        _traverse_to_project_root()

        build_spec = _get_yaml_dict(buildspec_name)

        # Assert that special beanstalk section exists
        if buildspec_config_header not in build_spec.keys():
            LOG.debug("Buildspec Keys: {0}".format(build_spec.keys()))
            raise ValidationError("Beanstalk configuration header '{0}' is missing from Buildspec file".format(buildspec_config_header))

        build_configuration = BuildConfiguration()
        beanstalk_build_configs = build_spec[buildspec_config_header]
        LOG.debug("EB Config Keys: {0}".format(beanstalk_build_configs.keys()))

        if service_role_key in beanstalk_build_configs.keys():
            build_configuration.service_role = beanstalk_build_configs[service_role_key]

        if image_key in beanstalk_build_configs.keys():
            build_configuration.image = beanstalk_build_configs[image_key]

        if compute_key in beanstalk_build_configs.keys():
            build_configuration.compute_type = beanstalk_build_configs[compute_key]

        if timeout_key in beanstalk_build_configs.keys():
            build_configuration.timeout = beanstalk_build_configs[timeout_key]

    finally:
        os.chdir(cwd)  # move back to working directory

    return build_configuration
示例#6
0
class TestDeployOperations(unittest.TestCase):
    app_name = 'ebcli-app'
    app_version_name = 'ebcli-app-version'
    env_name = 'ebcli-env'
    description = 'ebcli testing app'
    s3_bucket = 'app_bucket'
    s3_key = 'app_bucket_key'

    repository = 'my-repo'
    branch = 'my-branch'
    commit_id = '123456789'

    image = 'aws/codebuild/eb-java-8-amazonlinux-64:2.1.3'
    compute_type = 'BUILD_GENERAL1_SMALL'
    service_role = 'eb-test'
    timeout = 60
    build_config = BuildConfiguration(image=image,
                                      compute_type=compute_type,
                                      service_role=service_role,
                                      timeout=timeout)

    request_id = 'foo-foo-foo-foo'

    @mock.patch('ebcli.operations.deployops.elasticbeanstalk')
    @mock.patch('ebcli.operations.deployops.commonops')
    @mock.patch('ebcli.operations.deployops.gitops')
    @mock.patch('ebcli.operations.deployops.aws')
    @mock.patch('ebcli.operations.deployops.fileoperations')
    def test_plain_deploy(self, mock_fileops, mock_aws, mock_gitops,
                          mock_commonops, mock_beanstalk):
        mock_aws.get_region_name.return_value = 'us-east-1'
        mock_fileops.build_spec_exists.return_value = False
        mock_gitops.git_management_enabled.return_value = False
        mock_commonops.create_app_version.return_value = self.app_version_name
        mock_beanstalk.update_env_application_version.return_value = self.request_id

        deployops.deploy(self.app_name, self.env_name, None,
                         self.app_version_name, self.description)

        mock_commonops.create_app_version.assert_called_with(
            self.app_name,
            process=False,
            label=self.app_version_name,
            message=self.description,
            staged=False,
            build_config=None)
        mock_beanstalk.update_env_application_version.assert_called_with(
            self.env_name, self.app_version_name, None)
        mock_commonops.wait_for_success_events.assert_called_with(
            self.request_id,
            can_abort=True,
            env_name='ebcli-env',
            timeout_in_minutes=5,
        )

    @mock.patch('ebcli.operations.deployops.elasticbeanstalk')
    @mock.patch('ebcli.operations.deployops.commonops')
    @mock.patch('ebcli.operations.deployops.gitops')
    @mock.patch('ebcli.operations.deployops.aws')
    @mock.patch('ebcli.operations.deployops.fileoperations')
    def test_deploy_with_specified_version(self, mock_fileops, mock_aws,
                                           mock_gitops, mock_commonops,
                                           mock_beanstalk):
        mock_aws.get_region_name.return_value = 'us-east-1'
        mock_fileops.build_spec_exists.return_value = False
        mock_beanstalk.update_env_application_version.return_value = self.request_id

        deployops.deploy(self.app_name, self.env_name, self.app_version_name,
                         None, self.description)

        mock_commonops.create_app_version.assert_not_called()
        mock_beanstalk.update_env_application_version.assert_called_with(
            self.env_name, self.app_version_name, None)
        mock_commonops.wait_for_success_events.assert_called_with(
            self.request_id,
            can_abort=True,
            env_name='ebcli-env',
            timeout_in_minutes=5)

    @mock.patch('ebcli.operations.deployops.elasticbeanstalk')
    @mock.patch('ebcli.operations.deployops.commonops')
    @mock.patch('ebcli.operations.deployops.gitops')
    @mock.patch('ebcli.operations.deployops.aws')
    @mock.patch('ebcli.operations.deployops.fileoperations')
    def test_deployment_with_source(self, mock_fileops, mock_aws, mock_gitops,
                                    mock_commonops, mock_beanstalk):
        given_source = 'codecommit/test-repo/foo-branch'

        mock_aws.get_region_name.return_value = 'us-east-1'
        mock_fileops.build_spec_exists.return_value = False
        mock_commonops.create_app_version_from_source.return_value = self.app_version_name
        mock_commonops.wait_for_processed_app_versions.return_value = True
        mock_beanstalk.update_env_application_version.return_value = self.request_id

        deployops.deploy(self.app_name,
                         self.env_name,
                         None,
                         self.app_version_name,
                         self.description,
                         source=given_source,
                         timeout=10)

        mock_commonops.create_app_version_from_source.assert_called_with(
            self.app_name,
            given_source,
            process=False,
            label=self.app_version_name,
            message=self.description,
            build_config=None)
        mock_commonops.wait_for_processed_app_versions.assert_called_with(
            self.app_name, [self.app_version_name], timeout=10)
        mock_beanstalk.update_env_application_version.assert_called_with(
            self.env_name, self.app_version_name, None)
        mock_commonops.wait_for_success_events.assert_called_with(
            self.request_id,
            can_abort=True,
            env_name='ebcli-env',
            timeout_in_minutes=10)

    @mock.patch('ebcli.operations.deployops.elasticbeanstalk')
    @mock.patch('ebcli.operations.deployops.commonops')
    @mock.patch('ebcli.operations.deployops.gitops')
    @mock.patch('ebcli.operations.deployops.aws')
    @mock.patch('ebcli.operations.deployops.fileoperations')
    def test_deploy_with_codecommit(self, mock_fileops, mock_aws, mock_gitops,
                                    mock_commonops, mock_beanstalk):
        mock_aws.get_region_name.return_value = 'us-east-1'
        mock_fileops.build_spec_exists.return_value = False
        mock_gitops.git_management_enabled.return_value = True
        mock_commonops.wait_for_processed_app_versions.return_value = True
        mock_commonops.create_codecommit_app_version.return_value = self.app_version_name
        mock_beanstalk.update_env_application_version.return_value = self.request_id

        deployops.deploy(self.app_name, self.env_name, None,
                         self.app_version_name, self.description)

        mock_commonops.create_codecommit_app_version.assert_called_with(
            self.app_name,
            process=False,
            label=self.app_version_name,
            message=self.description,
            build_config=None)
        mock_commonops.wait_for_processed_app_versions.assert_called_with(
            self.app_name, [self.app_version_name], timeout=5)
        mock_beanstalk.update_env_application_version.assert_called_with(
            self.env_name, self.app_version_name, None)
        mock_commonops.wait_for_success_events.assert_called_with(
            self.request_id,
            can_abort=True,
            env_name='ebcli-env',
            timeout_in_minutes=5)

    @mock.patch('ebcli.operations.deployops.buildspecops')
    @mock.patch('ebcli.operations.deployops.elasticbeanstalk')
    @mock.patch('ebcli.operations.deployops.commonops')
    @mock.patch('ebcli.operations.deployops.gitops')
    @mock.patch('ebcli.operations.deployops.aws')
    @mock.patch('ebcli.operations.deployops.fileoperations')
    def test_plain_deploy_with_codebuild_buildspec(self, mock_fileops,
                                                   mock_aws, mock_gitops,
                                                   mock_commonops,
                                                   mock_beanstalk,
                                                   mock_buildspecops):
        mock_aws.get_region_name.return_value = 'us-east-1'
        mock_fileops.build_spec_exists.return_value = True
        mock_build_config = self.build_config
        mock_build_config.timeout = 60
        mock_fileops.get_build_configuration.return_value = mock_build_config
        mock_gitops.git_management_enabled.return_value = False
        mock_commonops.create_app_version.return_value = self.app_version_name
        mock_beanstalk.get_application_versions.return_value = [{
            "BuildArn":
            "arn::build:project"
        }]
        mock_beanstalk.update_env_application_version.return_value = self.request_id

        deployops.deploy(self.app_name, self.env_name, None,
                         self.app_version_name, self.description)

        mock_buildspecops.stream_build_configuration_app_version_creation.assert_called_with(
            self.app_name, self.app_version_name, mock_build_config)
        mock_commonops.create_app_version.assert_called_with(
            self.app_name,
            process=False,
            label=self.app_version_name,
            message=self.description,
            staged=False,
            build_config=mock_build_config)
        mock_beanstalk.update_env_application_version.assert_called_with(
            self.env_name, self.app_version_name, None)
        mock_commonops.wait_for_success_events.assert_called_with(
            self.request_id,
            can_abort=True,
            env_name='ebcli-env',
            timeout_in_minutes=5)
示例#7
0
class TestBuildSpecOps(unittest.TestCase):
    app_name = 'foo-app'
    version_label = 'foo-version-label'
    image = 'aws/codebuild/eb-java-7-amazonlinux-64:2.1.6'
    service_role = 'aws-codebuild-role'
    build_config = BuildConfiguration(image=image, service_role=service_role)
    app_version_raw_response = {
        'ApplicationVersions': [{
            u'ApplicationName':
            app_name,
            u'Status':
            'BUILDING',
            u'VersionLabel':
            version_label,
            u'SourceBuildInformation': {
                u'SourceLocation':
                'elasticbeanstalk-us-east-1-123456789098/cli-playground/app-598f-170216_154204.zip',
                u'SourceType': 'Zip',
                u'SourceRepository': 'S3'
            },
            u'Description':
            'Update paramter values',
            u'DateCreated':
            datetime(2017, 2, 16, 23, 42, 7, 341000),
            u'DateUpdated':
            datetime(2017, 2, 16, 23, 42, 7, 341000),
            u'SourceBundle': {},
            u'BuildArn':
            'arn:aws:codebuild:us-east-1:123456789098:build/Elastic-Beanstalk-cli-playground-app-598f-170216_154204-NfPSh:91f82fc0-9de0-4ed0-a458-dc317a7c1771'
        }]
    }
    build_response = {
        u'builds': [{
            u'buildComplete': False,
            u'logs': {
                u'groupName':
                u'/aws/codebuild/Elastic-Beanstalk-cli-playground-app-598f-170216_162002-rKVFA',
                u'deepLink':
                u'https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logEvent:group=/aws/codebuild/Elastic-Beanstalk-cli-playground-app-598f-170216_162002-rKVFA;stream=2e4c797a-440c-45e0-9992-eb2cfe0be161',
                u'streamName': u'2e4c797a-440c-45e0-9992-eb2cfe0be161'
            }
        }]
    }
    list_roles_response = [{
        u'AssumeRolePolicyDocument': {
            u'Version':
            u'2012-10-17',
            u'Statement': [{
                u'Action': u'sts:AssumeRole',
                u'Effect': u'Allow',
                u'Principal': {
                    u'Service': u'codebuild.amazonaws.com'
                }
            }]
        },
        u'RoleName':
        'aws-codebuild-role',
        u'Path':
        '/service-role/',
        u'Arn':
        'arn:aws:iam::123456789098:role/service-role/aws-codebuild-role'
    }]

    def setUp(self):
        self.patcher_beanstalk = mock.patch(
            'ebcli.operations.buildspecops.elasticbeanstalk')
        self.patcher_codebuild = mock.patch(
            'ebcli.operations.buildspecops.codebuild')
        self.mock_beanstalk = self.patcher_beanstalk.start()
        self.mock_codebuild = self.patcher_codebuild.start()

    def tearDown(self):
        self.patcher_beanstalk.stop()
        self.patcher_codebuild.stop()

    @mock.patch('ebcli.operations.commonops.wait_for_success_events')
    @mock.patch('ebcli.operations.buildspecops.wait_for_app_version_attribute')
    def test_stream_build_config_app_creation_happy_case(
            self, mock_wait_attribute, mock_success_events):
        mock_wait_attribute.return_value = True
        self.mock_beanstalk.get_application_versions.return_value = self.app_version_raw_response
        self.mock_codebuild.batch_get_builds.return_value = self.build_response

        build_spec = MagicMock()
        build_spec.timeout = 364

        buildspecops.stream_build_configuration_app_version_creation(
            self.app_name, self.version_label, build_spec)

        mock_wait_attribute.assert_called_with(self.app_name,
                                               [self.version_label],
                                               timeout=1)
        self.mock_beanstalk.get_application_versions.assert_called_with(
            self.app_name, version_labels=[self.version_label])
        self.mock_codebuild.batch_get_builds.assert_called_with([
            self.app_version_raw_response['ApplicationVersions'][0]['BuildArn']
        ])

        timeout_error_message = ' '.join([
            'The CodeBuild build timed out after 364 minute(s).',
            "To increase the time limit, use the 'Timeout' option in the 'buildspec.yml' file."
        ])
        mock_success_events.assert_called_with(
            app_name='foo-app',
            can_abort=False,
            request_id=None,
            timeout_error_message=timeout_error_message,
            timeout_in_minutes=364,
            version_label=self.version_label)

    @mock.patch('ebcli.operations.commonops.wait_for_success_events')
    @mock.patch('ebcli.operations.buildspecops.wait_for_app_version_attribute')
    def test_stream_build_config_app_creation__no_timeout_specified__timeout_defaults_to_60_Minutes(
            self, mock_wait_attribute, mock_success_events):
        mock_wait_attribute.return_value = True
        self.mock_beanstalk.get_application_versions.return_value = self.app_version_raw_response
        self.mock_codebuild.batch_get_builds.return_value = self.build_response

        build_spec = MagicMock()
        build_spec.timeout = None

        buildspecops.stream_build_configuration_app_version_creation(
            self.app_name, self.version_label, build_spec)

        mock_wait_attribute.assert_called_with(self.app_name,
                                               [self.version_label],
                                               timeout=1)
        self.mock_beanstalk.get_application_versions.assert_called_with(
            self.app_name, version_labels=[self.version_label])
        self.mock_codebuild.batch_get_builds.assert_called_with([
            self.app_version_raw_response['ApplicationVersions'][0]['BuildArn']
        ])

        timeout_error_message = ' '.join([
            'The CodeBuild build timed out after 60 minute(s).',
            "To increase the time limit, use the 'Timeout' option in the 'buildspec.yml' file."
        ])
        mock_success_events.assert_called_with(
            app_name='foo-app',
            can_abort=False,
            request_id=None,
            timeout_error_message=timeout_error_message,
            timeout_in_minutes=60,
            version_label=self.version_label)

    @mock.patch('ebcli.operations.commonops.wait_for_success_events')
    @mock.patch('ebcli.operations.buildspecops.wait_for_app_version_attribute')
    def test_stream_build_config_app_creation_no_build_arn_failed_to_create(
            self, mock_wait_attribute, mock_success_events):
        mock_wait_attribute.return_value = False
        self.mock_beanstalk.get_application_versions.return_value = self.app_version_raw_response
        mock_success_events.side_effect = ServiceError(
            'Failed to create application version')

        build_spec = MagicMock()
        build_spec.timeout = 60

        self.assertRaises(
            ServiceError,
            buildspecops.stream_build_configuration_app_version_creation,
            self.app_name, self.version_label, build_spec)

        mock_wait_attribute.assert_called_with(self.app_name,
                                               [self.version_label],
                                               timeout=1)
        self.mock_beanstalk.get_application_versions.assert_called_with(
            self.app_name, version_labels=[self.version_label])
        self.mock_codebuild.batch_get_builds.assert_not_called()

        timeout_error_message = ' '.join([
            'The CodeBuild build timed out after 60 minute(s).',
            "To increase the time limit, use the 'Timeout' option in the 'buildspec.yml' file."
        ])
        mock_success_events.assert_called_with(
            app_name='foo-app',
            can_abort=False,
            request_id=None,
            timeout_error_message=timeout_error_message,
            timeout_in_minutes=60,
            version_label=self.version_label)
        self.mock_beanstalk.delete_application_version.assert_called_with(
            self.app_name, self.version_label)

    def test_validate_build_config_without_service_role(self):
        build_config = copy.deepcopy(self.build_config)
        build_config.service_role = None
        build_config.timeout = 60

        self.assertRaises(ValidationError, buildspecops.validate_build_config,
                          build_config)

    @mock.patch('ebcli.lib.iam.get_roles')
    def test_validate_build_config_without_image(self, mock_get_roles):
        build_config = copy.deepcopy(self.build_config)
        build_config.image = None
        mock_get_roles.return_value = self.list_roles_response

        self.assertRaises(ValidationError, buildspecops.validate_build_config,
                          build_config)

        mock_get_roles.assert_called_with()

    @mock.patch('ebcli.lib.iam.get_roles')
    def test_validate_build_config_with_invalid_role(self, mock_get_roles):
        build_config = copy.deepcopy(self.build_config)
        build_config.service_role = 'bad-role'
        mock_get_roles.return_value = self.list_roles_response

        self.assertRaises(ValidationError, buildspecops.validate_build_config,
                          build_config)

        mock_get_roles.assert_called_with()

    @mock.patch('ebcli.lib.iam.get_roles')
    def test_validate_build_config_happy_case(self, mock_get_roles):
        mock_get_roles.return_value = self.list_roles_response

        buildspecops.validate_build_config(self.build_config)

        mock_get_roles.assert_called_with()

    @mock.patch(
        'ebcli.operations.buildspecops.elasticbeanstalk.get_application_versions'
    )
    @mock.patch('ebcli.operations.buildspecops.io.log_error')
    @mock.patch('ebcli.operations.buildspecops._timeout_reached')
    @mock.patch('ebcli.operations.buildspecops._sleep')
    def test_wait_for_app_version_attribute__some_application_versions_failed_to_contain_build_arn_attribute(
            self, _sleep_mock, _timeout_reached_mock, log_error_mock,
            get_application_versions_mock):
        _sleep_mock.side_effect = None
        _timeout_reached_mock.return_value = False
        get_application_versions_mock.side_effect = [
            {
                "ApplicationVersions": []
            },
            {
                "ApplicationVersions": [{
                    "VersionLabel": "version-label-1",
                    "Status": 'PROCESSING',
                }, {
                    "VersionLabel": "version-label-2",
                    "Status": 'PROCESSING',
                }]
            },
            {
                "ApplicationVersions": [{
                    "VersionLabel": "version-label-1",
                    "Status": 'PROCESSED',
                    "BuildArn": 'codebuild-build-arn'
                }, {
                    "VersionLabel": "version-label-2",
                    "Status": 'FAILED',
                }]
            },
        ]

        self.assertFalse(
            buildspecops.wait_for_app_version_attribute(
                'my-application', ['version-label-1', 'version-label-2']))
        log_error_mock.assert_has_calls([
            mock.call(
                'Application Version version-label-2 has failed to generate required attributes.'
            )
        ])

    @mock.patch(
        'ebcli.operations.buildspecops.elasticbeanstalk.get_application_versions'
    )
    @mock.patch('ebcli.operations.buildspecops.io.log_error')
    @mock.patch('ebcli.operations.buildspecops._timeout_reached')
    @mock.patch('ebcli.operations.buildspecops._sleep')
    def test_wait_for_app_version_attribute__all_app_versions_contain_build_arn(
            self, _sleep_mock, _timeout_reached_mock, log_error_mock,
            get_application_versions_mock):
        _sleep_mock.side_effect = None
        _timeout_reached_mock.return_value = False
        get_application_versions_mock.side_effect = [
            {
                "ApplicationVersions": []
            },
            {
                "ApplicationVersions": [{
                    "VersionLabel": "version-label-1",
                    "Status": 'PROCESSING',
                }, {
                    "VersionLabel": "version-label-2",
                    "Status": 'PROCESSING',
                }]
            },
            {
                "ApplicationVersions": [{
                    "VersionLabel": "version-label-1",
                    "Status": 'PROCESSED',
                    "BuildArn": "build-arn-1"
                }, {
                    "VersionLabel": "version-label-2",
                    "Status": 'PROCESSED',
                    "BuildArn": "build-arn-2"
                }]
            },
        ]

        self.assertTrue(
            buildspecops.wait_for_app_version_attribute(
                'my-application', ['version-label-1', 'version-label-2']))
        log_error_mock.assert_not_called()

    @mock.patch(
        'ebcli.operations.buildspecops.elasticbeanstalk.get_application_versions'
    )
    @mock.patch('ebcli.operations.buildspecops.io.log_error')
    @mock.patch('ebcli.operations.buildspecops._timeout_reached')
    @mock.patch('ebcli.operations.buildspecops._sleep')
    def test_wait_for_app_version_attribute__timeout_reached(
            self, _sleep_mock, _timeout_reached_mock, log_error_mock,
            get_application_versions_mock):
        _sleep_mock.side_effect = None
        _timeout_reached_mock.return_value = True
        get_application_versions_mock.side_effect = mock.MagicMock()

        self.assertFalse(
            buildspecops.wait_for_app_version_attribute(
                'my-application', ['version-label-1', 'version-label-2']))
        log_error_mock.assert_called_once_with(
            'Application Version version-label-1, version-label-2 has failed to generate required attributes.'
        )

    def test_wait_for_app_version_attribute__no_app_versions_to_wait_for(self):
        self.assertTrue(
            buildspecops.wait_for_app_version_attribute('my-application', []))