Esempio n. 1
0
def stream_build_configuration_app_version_creation(app_name,
                                                    app_version_label):
    # Get the CloudWatch logs link
    successfully_generated = wait_for_app_version_attribute(
        app_name, [app_version_label], 'BuildArn', 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

    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)
    else:
        io.log_warning("Could not retrieve CloudWatch link for CodeBuild logs")

    # Wait for the success events
    try:
        from ebcli.operations.commonops import wait_for_success_events
        wait_for_success_events(None,
                                timeout_in_minutes=5,
                                can_abort=False,
                                version_label=app_version_label)
    except ServiceError as ex:
        LOG.debug(
            "Caught service error while creating application version '{0}' "
            "deleting the created applicaiton version as it is useless now.")
        elasticbeanstalk.delete_application_version(app_name,
                                                    app_version_label)
        raise ex
 def test_log_warning__cement_application_not_defined_yet(
         self,
         echo_mock,
         ebglobals_mock
 ):
     io.log_warning('hello, world!')
     echo_mock.assert_called_once_with('WARN: hello, world!')
Esempio n. 3
0
def interactive_update_lifcycle_policy(app_name):
    # Get current application settings
    api_model = elasticbeanstalk.describe_application(app_name)

    # Convert into yaml format from raw API
    lifecycle_config = LifecycleConfiguration(api_model)
    usr_model = lifecycle_config.convert_api_to_usr_model()

    # Save yaml file into temp file
    file_location = fileoperations.save_app_file(usr_model)
    fileoperations.open_file_for_editing(file_location)

    # Update and delete file
    try:
        usr_model = fileoperations.get_application_from_file(app_name)
        config_changes = lifecycle_config.collect_changes(usr_model)
        fileoperations.delete_app_file(app_name)
    except InvalidSyntaxError:
        io.log_error(strings['lifecycle.invalidsyntax'])
        fileoperations.delete_app_file(app_name)
        return

    if not config_changes:
        # no changes made, exit
        io.log_warning(strings['lifecycle.updatenochanges'])
        return

    elasticbeanstalk.update_application_resource_lifecycle(app_name, config_changes)
    io.echo(strings['lifecycle.success'])
Esempio n. 4
0
    def setup_new_codecommit_branch(self, branch_name):
        LOG.debug("Setting up CodeCommit branch")

        self.fetch_remote_branches(self.codecommit_remote_name)

        self.checkout_branch(branch_name, create_branch=True)

        stdout, stderr, exitcode = self._run_cmd(
            ['git', 'push', '-u', self.codecommit_remote_name, branch_name],
            handle_exitcode=False)

        if exitcode == 1:
            io.log_warning(
                'Git is not able to push code: {0}'.format(exitcode))
            io.log_warning(stderr)

        if stderr:
            LOG.debug('git push error: ' + stderr)

        LOG.debug('git push result: ' + stdout)

        self.fetch_remote_branches(self.codecommit_remote_name)

        stdout, stderr, exitcode = self._run_cmd([
            'git', 'branch', '--set-upstream-to', '{0}/{1}'.format(
                self.codecommit_remote_name, branch_name)
        ],
                                                 handle_exitcode=False)

        if stderr:
            LOG.debug('git branch --set-upstream-to error: ' + stderr)

        LOG.debug('git branch result: ' + stdout)
Esempio n. 5
0
def validate_build_config(build_config):
    if build_config.service_role is not None:
        # Verify that the service role exists in the customers account
        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']

        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."
        )
        # Fail because the service role is required
    if build_config.image is None:
        #  Fail because the image is required
        raise ValidationError(
            "No image specified in buildspec; this is a required argument.")
Esempio n. 6
0
def interactive_update_lifcycle_policy(app_name):
    # Get current application settings
    api_model = elasticbeanstalk.describe_application(app_name)

    # Convert into yaml format from raw API
    lifecycle_config = LifecycleConfiguration(api_model)
    usr_model = lifecycle_config.convert_api_to_usr_model()

    # Save yaml file into temp file
    file_location = fileoperations.save_app_file(usr_model)
    fileoperations.open_file_for_editing(file_location)

    # Update and delete file
    try:
        usr_model = fileoperations.get_application_from_file(app_name)
        config_changes = lifecycle_config.collect_changes(usr_model)
        fileoperations.delete_app_file(app_name)
    except InvalidSyntaxError:
        io.log_error(strings['lifecycle.invalidsyntax'])
        fileoperations.delete_app_file(app_name)
        return

    if not config_changes:
        # no changes made, exit
        io.log_warning(strings['lifecycle.updatenochanges'])
        return

    elasticbeanstalk.update_application_resource_lifecycle(
        app_name, config_changes)
    io.echo(strings['lifecycle.success'])
def interactive_update_lifcycle_policy(app_name):
    api_model = elasticbeanstalk.describe_application(app_name)

    lifecycle_config = LifecycleConfiguration(api_model)
    usr_model = lifecycle_config.convert_api_to_usr_model()

    file_location = fileoperations.save_app_file(usr_model)
    fileoperations.open_file_for_editing(file_location)

    try:
        usr_model = fileoperations.get_application_from_file(app_name)
        config_changes = lifecycle_config.collect_changes(usr_model)
        fileoperations.delete_app_file(app_name)
    except InvalidSyntaxError:
        io.log_error(strings['lifecycle.invalidsyntax'])
        fileoperations.delete_app_file(app_name)
        return

    if not config_changes:
        io.log_warning(strings['lifecycle.updatenochanges'])
        return

    elasticbeanstalk.update_application_resource_lifecycle(
        app_name, config_changes)
    io.echo(strings['lifecycle.success'])
def do_upgrade(env_name,
               add_rolling,
               timeout,
               solution_stack_name,
               health_based=False,
               platform_arn=None):
    if add_rolling:
        if health_based:
            roll_type = 'Health'
        else:
            roll_type = 'Time'
        changes = [
            elasticbeanstalk.create_option_setting(
                namespaces.ROLLING_UPDATES,
                option_names.ROLLING_UPDATE_ENABLED, 'true'),
            elasticbeanstalk.create_option_setting(
                namespaces.ROLLING_UPDATES, option_names.ROLLING_UPDATE_TYPE,
                roll_type)
        ]
        io.log_warning(prompts['upgrade.applyrolling'].format(roll_type))
    else:
        changes = None

    if PlatformVersion.is_valid_arn(platform_arn):
        commonops.update_environment(env_name,
                                     changes,
                                     None,
                                     timeout=timeout,
                                     platform_arn=platform_arn)
    else:
        commonops.update_environment(env_name,
                                     changes,
                                     None,
                                     timeout=timeout,
                                     solution_stack_name=solution_stack_name)
 def test_log_warning(
         self,
         echo_mock,
         ebglobals_mock
 ):
     io.log_warning('hello, world!')
     echo_mock.assert_not_called()
def remove_cwl_extensions(cwfile_dir, ebextension_dir):
    for file_name in os.listdir(cwfile_dir):
        try:
            os.remove(os.path.join(ebextension_dir, file_name))
        except:
            io.log_warning('Ebextension {} already removed'.format(file_name))

    io.echo(strings['cloudwatch-setup.removetext'])
Esempio n. 11
0
def create_codecommit_app_version(app_name,
                                  process=False,
                                  label=None,
                                  message=None,
                                  build_config=None):
    fileoperations.ProjectRoot.traverse()

    source_control = SourceControl.get_source_control()
    if source_control.get_current_commit() is None:
        io.log_warning(
            'There are no commits for the current branch, attempting '
            'to create an empty commit and launching with the sample '
            'application')
        source_control.create_initial_commit()

    if source_control.untracked_changes_exist():
        io.log_warning(strings['sc.unstagedchanges'])

    if label:
        version_label = label
    else:
        version_label = source_control.get_version_label()

    if message:
        description = message
    else:
        description = source_control.get_message()

    if len(description) > 200:
        description = description[:195] + '...'

    try:
        source_control.push_codecommit_code()
    except CommandError as e:
        io.echo("Could not push code to the CodeCommit repository:")
        raise e

    from ebcli.operations import gitops
    repository = gitops.get_default_repository()
    commit_id = source_control.get_current_commit()

    if repository is None or commit_id is None:
        raise ServiceError(
            "Could not find repository or commit id to create an application version"
        )

    io.log_info('Creating AppVersion ' + version_label)
    return _create_application_version(app_name,
                                       version_label,
                                       description,
                                       None,
                                       None,
                                       process,
                                       repository=repository,
                                       commit_id=commit_id,
                                       build_config=build_config)
Esempio n. 12
0
    def push_codecommit_code(self):
        io.log_info('Pushing local code to codecommit with git-push')

        stdout, stderr, exitcode = self._run_cmd(['git', 'push', self.get_current_repository(), self.get_current_branch()])

        if exitcode != 0:
            io.log_warning('Git is not able to push code: {0}'.format(exitcode))
            io.log_warning(stderr)
        else:
            LOG.debug('git push result: {0}'.format(stdout))
            self._handle_exitcode(exitcode, stderr)
Esempio n. 13
0
    def do_command(self):
        app_name = self.get_app_name()
        num_to_leave = self.app.pargs.num_to_leave
        older_than = self.app.pargs.older_than
        force = self.app.pargs.force

        envs = elasticbeanstalk.get_app_environments(app_name)
        versions_in_use = [e.version_label for e in envs]

        app_versions = elasticbeanstalk.get_application_versions(
            app_name)['ApplicationVersions']
        app_versions.sort(key=itemgetter('DateUpdated'), reverse=True)

        # Filter out versions currently being used
        app_versions = [
            v for v in app_versions if v['VersionLabel'] not in versions_in_use
        ]

        total_num_unused_versions = len(app_versions)

        if total_num_unused_versions < num_to_leave:
            io.echo(
                'Not enough unused application version to leave behind {0}; No application versions to delete.'
                .format(num_to_leave))
            return

        # Filter out versions newer than filter date
        app_versions = [
            v for v in app_versions if utils.get_delta_from_now_and_datetime(
                v['DateUpdated']).days > older_than
        ]

        # dont include most recent
        app_versions = app_versions[num_to_leave:]

        if app_versions:
            if not force:
                response = io.get_boolean_response(
                    '{} application versions will be deleted. '
                    'Continue?'.format(len(app_versions)))
                if not response:
                    return
        else:
            io.echo('No application versions to delete.')
            return

        for version in app_versions:
            label = version['VersionLabel']
            try:
                elasticbeanstalk.delete_application_version(app_name, label)
            except ServiceError as e:
                io.log_warning('Error deleting version {0}. Error: {1}'.format(
                    label, e.message))
    def get_instance_profile(self):
        # Check to see if it was specified on the command line
        profile = self.app.pargs.instance_profile

        if profile is None:
            try:
                # Check to see if it is associated with the workspace
                profile = fileoperations.get_instance_profile()
            except NotInitializedError:
                pass

        if profile is None:
            # Check to see if the default instance profile already exists
            try:
                existing_profiles = iam.get_instance_profile_names()
                if iam_attributes.DEFAULT_PLATFORM_BUILDER_ROLE in existing_profiles:
                    profile = iam_attributes.DEFAULT_PLATFORM_BUILDER_ROLE
            except NotAuthorizedError:
                io.log_warning(strings['platformcreateiamdescribeerror.info'])

        if profile is None:
            # We will now create the default role for the customer
            try:
                profile = iam_attributes.DEFAULT_PLATFORM_BUILDER_ROLE
                try:
                    iam.create_instance_profile(profile)
                    io.log_info(strings['platformcreateiamcreated.info'])
                except AlreadyExistsError:
                    pass

                document = iam_documents.EC2_ASSUME_ROLE_PERMISSION
                try:
                    # Create a role with the same name
                    iam.create_role(profile, document)

                    # Attach required custom platform builder permissions
                    iam.put_role_policy(
                        profile,
                        iam_attributes.PLATFORM_BUILDER_INLINE_POLICY_NAME,
                        iam_documents.CUSTOM_PLATFORM_BUILDER_INLINE_POLICY)
                    # Associate instance profile with the required role
                    iam.add_role_to_profile(profile, profile)
                    io.log_info(strings['platformcreateiampolicyadded.info'])
                except AlreadyExistsError:
                    # If the role exists then we leave it as is, we do not try to add or modify its policies
                    pass

            except NotAuthorizedError:
                io.log_warning(strings['platformcreateiamcreateerror.info'])

        # Save to disk
        write_config_setting('global', 'instance_profile', profile)
Esempio n. 15
0
    def get_current_branch(self):
        stdout, stderr, exitcode = self._run_cmd(
            ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
            handle_exitcode=False)

        if stdout.strip() == 'HEAD':
            io.log_warning(
                'Git is in a detached head state. Using branch "default".')
            return 'default'
        else:
            self._handle_exitcode(exitcode, stderr)

        return stdout
Esempio n. 16
0
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
Esempio n. 17
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

        build_configuration = BuildConfiguration()
        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 build_configuration

        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
Esempio n. 18
0
def setup_ssh(env_name, keyname, timeout=None):
    io.log_warning(prompts['ssh.setupwarn'].replace('{env-name}', env_name))

    keyname = prompt_for_ec2_keyname(env_name=env_name, keyname=keyname)

    if keyname:
        options = [{
            'Namespace': 'aws:autoscaling:launchconfiguration',
            'OptionName': 'EC2KeyName',
            'Value': keyname
        }]
        commonops.update_environment(env_name,
                                     options,
                                     False,
                                     timeout=timeout or 5)
Esempio n. 19
0
    def get_current_branch(self):
        revparse_command = ['git', 'rev-parse', '--abbrev-ref', 'HEAD']
        LOG.debug('Getting current branch name by performing `{0}`'.format(' '.join(revparse_command)))

        stdout, stderr, exitcode = self._run_cmd(revparse_command, handle_exitcode=False)

        if stdout.strip() == 'HEAD':
            io.log_warning('Git is in a detached head state. Using branch "default".')
            return 'default'
        else:
            self._handle_exitcode(exitcode, stderr)

        LOG.debug(stdout)

        return stdout
Esempio n. 20
0
    def get_current_branch(self):
        revparse_command = ['git', 'rev-parse', '--abbrev-ref', 'HEAD']
        LOG.debug('Getting current branch name by performing `{0}`'.format(' '.join(revparse_command)))

        stdout, stderr, exitcode = self._run_cmd(revparse_command, handle_exitcode=False)

        if stdout.strip() == 'HEAD':
            io.log_warning('Git is in a detached head state. Using branch "default".')
            return 'default'
        else:
            self._handle_exitcode(exitcode, stderr)

        LOG.debug(stdout)

        return stdout
Esempio n. 21
0
def get_platform(solution_string, iprofile=None):
    """
    Set a PlatformVersion or a SolutionStack based on the `solution_string`.
    :param solution_string: The value of the `--platform` argument input by the customer
    :param iprofile: The instance profile, if any, the customer passed as argument
    :return: a PlatformVersion or a SolutionStack object depending on whether the match was
        against an ARN of a Solution Stack name.
    """
    solution = solution_stack_ops.find_solution_stack_from_string(solution_string)
    solution = solution or solution_stack_ops.get_solution_stack_from_customer()

    if isinstance(solution, SolutionStack):
        if solution.language_name == 'Multi-container Docker' and not iprofile:
            io.log_warning(prompts['ecs.permissions'])

    return solution
def create_platform_version(
    version,
    major_increment,
    minor_increment,
    patch_increment,
    instance_type,
    vpc=None,
    staged=False,
    timeout=None,
    tags=None,
):

    _raise_if_directory_is_empty()
    _raise_if_platform_definition_file_is_missing()
    version and _raise_if_version_format_is_invalid(version)
    platform_name = fileoperations.get_platform_name()
    instance_profile = fileoperations.get_instance_profile(None)
    key_name = commonops.get_default_keyname()
    version = version or _resolve_version_number(
        platform_name, major_increment, minor_increment, patch_increment)
    tags = tagops.get_and_validate_tags(tags)
    source_control = SourceControl.get_source_control()
    io.log_warning(strings['sc.unstagedchanges']
                   ) if source_control.untracked_changes_exist() else None
    version_label = _resolve_version_label(source_control, staged)
    bucket, key, file_path = _resolve_s3_bucket_and_key(
        platform_name, version_label, source_control, staged)
    _upload_platform_version_to_s3_if_necessary(bucket, key, file_path)
    io.log_info('Creating Platform Version ' + version_label)
    response = elasticbeanstalk.create_platform_version(
        platform_name, version, bucket, key, instance_profile, key_name,
        instance_type, tags, vpc)

    environment_name = 'eb-custom-platform-builder-packer'

    io.echo(
        colored(
            strings['platformbuildercreation.info'].format(environment_name),
            attrs=['reverse']))

    fileoperations.update_platform_version(version)
    commonops.set_environment_for_current_branch(environment_name)

    stream_platform_logs(response, platform_name, version, timeout)
Esempio n. 23
0
def download_and_extract_sample_app(env_name):
    """
    Method orchestrates the retrieval, and extraction of application version.

    :param env_name: The name of the environment whose application version will be downloaded.
    :return: None
    """
    try:
        url = retrieve_application_version_url(env_name)
        zip_file_location = '.elasticbeanstalk/.sample_app_download.zip'
        io.echo('INFO: {}'.format(strings['create.downloading_sample_application']))
        download_application_version(url, zip_file_location)
        ZipFile(zip_file_location, 'r', allowZip64=True).extractall()
        os.remove(zip_file_location)
        io.echo('INFO: {}'.format(strings['create.sample_application_download_complete']))
    except NotAuthorizedError as e:
        io.log_warning('{} Continuing environment creation.'.format(e.message))
    except cloudformation.CFNTemplateNotFound as e:
        io.log_warning('{} Continuing environment creation.'.format(e.message))
def cleanup_application_versions(app_name):
    io.echo('Removing application versions from s3.')
    versions = elasticbeanstalk.get_application_versions(
        app_name)['ApplicationVersions']
    buckets = defaultdict(list)
    for version in versions:
        bundle = version.get('SourceBundle', {})
        bucket = bundle.get('S3Bucket')
        key = bundle.get('S3Key')
        if bucket and key:
            buckets[bucket].append(key)

    for bucket, keys in six.iteritems(buckets):
        try:
            s3.delete_objects(bucket, keys)
        except NotAuthorizedError:
            io.log_warning(
                'Error deleting application versions from bucket "{0}"'.format(
                    bucket))
Esempio n. 25
0
def download_and_extract_sample_app(env_name):
    """
    Method orchestrates the retrieval, and extraction of application version.

    :param env_name: The name of the environment whose application version will be downloaded.
    :return: None
    """
    try:
        url = retrieve_application_version_url(env_name)
        zip_file_location = '.elasticbeanstalk/.sample_app_download.zip'
        io.echo('INFO: {}'.format(strings['create.downloading_sample_application']))
        download_application_version(url, zip_file_location)
        ZipFile(zip_file_location, 'r', allowZip64=True).extractall()
        os.remove(zip_file_location)
        io.echo('INFO: {}'.format(strings['create.sample_application_download_complete']))
    except NotAuthorizedError as e:
        io.log_warning('{} Continuing environment creation.'.format(e.message))
    except cloudformation.CFNTemplateNotFound as e:
        io.log_warning('{} Continuing environment creation.'.format(e.message))
Esempio n. 26
0
    def setup_new_codecommit_branch(self, branch_name):
        LOG.debug("Setting up CodeCommit branch")

        # Get fetch to ensure the remote repository is up to date
        self.fetch_remote_branches(self.codecommit_remote_name)

        # Attempt to check out the desired branch, if it doesn't exist create it from the current HEAD
        self.checkout_branch(branch_name, create_branch=True)

        # Push the current code and set the remote as the current working remote
        stdout, stderr, exitcode = self._run_cmd(
            ['git', 'push', '-u', self.codecommit_remote_name, branch_name],
            handle_exitcode=False
        )

        if exitcode == 1:
            io.log_warning('Git is not able to push code: {0}'.format(exitcode))
            io.log_warning(stderr)

        if stderr:
            LOG.debug('git push error: ' + stderr)

        LOG.debug('git push result: ' + stdout)

        # Get fetch to ensure the remote repository is up to date because we just pushed a new branch
        self.fetch_remote_branches(self.codecommit_remote_name)

        # Set the remote branch up so it's not using the presigned remote OR if the push failed.
        stdout, stderr, exitcode = self._run_cmd(
            [
                'git',
                'branch',
                '--set-upstream-to',
                '{0}/{1}'.format(self.codecommit_remote_name, branch_name)
            ],
            handle_exitcode=False
        )

        if stderr:
            LOG.debug('git branch --set-upstream-to error: ' + stderr)

        LOG.debug('git branch result: ' + stdout)
Esempio n. 27
0
def update_environment_configuration(app_name, env_name, nohang, timeout=None):
    # get environment setting
    api_model = elasticbeanstalk.describe_configuration_settings(
        app_name, env_name)

    # Convert the raw api return to yaml format
    env_settings = EnvironmentSettings(api_model)
    usr_model = env_settings.convert_api_to_usr_model()

    # Save the yaml in a temp file
    file_location = fileoperations.save_env_file(usr_model)
    fileoperations.open_file_for_editing(file_location)

    platform_arn = None

    # Update and delete file
    try:
        usr_model = fileoperations.get_environment_from_file(env_name)
        changes, remove = env_settings.collect_changes(usr_model)
        if api_model['PlatformArn'] != usr_model['PlatformArn']:
            platform_arn = usr_model['PlatformArn']
        fileoperations.delete_env_file(env_name)
    except InvalidSyntaxError:
        io.log_error(prompts['update.invalidsyntax'])
        return

    if not changes and not remove and not platform_arn:
        # no changes made, exit
        io.log_warning('No changes made. Exiting.')
        return

    if fileoperations.env_yaml_exists():
        io.echo(strings['config.envyamlexists'])

    commonops.update_environment(env_name,
                                 changes,
                                 nohang,
                                 remove=remove,
                                 timeout=timeout,
                                 solution_stack_name=None,
                                 platform_arn=platform_arn)
def upgrade_env(app_name, env_name, timeout, confirm, noroll):
    env = elasticbeanstalk.get_environment_settings(app_name, env_name)
    latest = solution_stack_ops.find_solution_stack_from_string(
        env.platform.name, find_newer=True)

    if latest.name == env.platform.name:
        io.echo(prompts['upgrade.alreadylatest'])
        return
    else:
        single = elasticbeanstalk.get_option_setting(
            env.option_settings, namespaces.ENVIRONMENT,
            'EnvironmentType') == 'SingleInstance'
        rolling_enabled = elasticbeanstalk.get_option_setting(
            env.option_settings, namespaces.ROLLING_UPDATES,
            option_names.ROLLING_UPDATE_ENABLED) == 'true'
        webserver = env.tier.name.lower() == 'webserver'

        io.echo()
        io.echo(prompts['upgrade.infodialog'].format(env_name))
        io.echo('Current platform:', env.platform)
        io.echo('Latest platform: ', latest.name)
        io.echo()

        warning = _get_warning_message(confirm, single, rolling_enabled,
                                       webserver, noroll)
        if warning:
            io.log_warning(warning)
            io.echo(prompts['upgrade.altmessage'])
            io.echo()

        if not confirm:
            io.validate_action(prompts['upgrade.validate'], env.name)

        add_rolling = _should_add_rolling(single, rolling_enabled, noroll)

        do_upgrade(env_name,
                   add_rolling,
                   timeout,
                   latest.name,
                   health_based=webserver,
                   platform_arn=latest.name)
Esempio n. 29
0
def scale(app_name, env_name, number, confirm, timeout=None):
    options = []
    # get environment
    env = elasticbeanstalk.describe_configuration_settings(
        app_name, env_name)['OptionSettings']

    # if single instance, offer to switch to load-balanced
    namespace = 'aws:elasticbeanstalk:environment'
    setting = next((n for n in env if n["Namespace"] == namespace), None)
    value = setting['Value']
    if value == 'SingleInstance':
        if not confirm:
            ## prompt to switch to LoadBalanced environment type
            io.echo(prompts['scale.switchtoloadbalance'])
            io.log_warning(prompts['scale.switchtoloadbalancewarn'])
            switch = io.get_boolean_response()
            if not switch:
                return

        options.append({
            'Namespace': namespace,
            'OptionName': 'EnvironmentType',
            'Value': 'LoadBalanced'
        })

    # change autoscaling min AND max to number
    namespace = 'aws:autoscaling:asg'
    max = 'MaxSize'
    min = 'MinSize'

    for name in [max, min]:
        options.append({
            'Namespace': namespace,
            'OptionName': name,
            'Value': str(number)
        })
    request_id = elasticbeanstalk.update_environment(env_name, options)

    commonops.wait_for_success_events(request_id,
                                      timeout_in_minutes=timeout or 5,
                                      can_abort=True)
Esempio n. 30
0
def update_environment_configuration(app_name, env_name, nohang,
                                     timeout=None):
    # get environment setting
    api_model = elasticbeanstalk.describe_configuration_settings(
        app_name, env_name
    )

    # Convert the raw api return to yaml format
    env_settings = EnvironmentSettings(api_model)
    usr_model = env_settings.convert_api_to_usr_model()

    # Save the yaml in a temp file
    file_location = fileoperations.save_env_file(usr_model)
    fileoperations.open_file_for_editing(file_location)

    platform_arn = None

    # Update and delete file
    try:
        usr_model = fileoperations.get_environment_from_file(env_name)
        changes, remove = env_settings.collect_changes(usr_model)
        if api_model['PlatformArn'] != usr_model['PlatformArn']:
            platform_arn = usr_model['PlatformArn']
        fileoperations.delete_env_file(env_name)
    except InvalidSyntaxError:
        io.log_error(prompts['update.invalidsyntax'])
        return

    if not changes and not remove and not platform_arn:
        # no changes made, exit
        io.log_warning('No changes made. Exiting.')
        return

    if fileoperations.env_yaml_exists():
        io.echo(strings['config.envyamlexists'])

    commonops.update_environment(env_name, changes, nohang,
                                 remove=remove, timeout=timeout,
                                 solution_stack_name=None,
                                 platform_arn=platform_arn)
Esempio n. 31
0
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], 'BuildArn', 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")

    # Wait for the success events
    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
Esempio n. 32
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
Esempio n. 33
0
def retrieve_application_version_url(env_name):
    """
    Method retrieves the URL of the application version of the environment, 'env_name',
    for the CLI to download from.

    The method waits for the CloudFormation stack associated with `env_name` to come
    into existence, after which, it retrieves the 'url' of the application version.

    :param env_name: Name of the environment that launched with the sample application
    :return: The URL of the application version.
    """
    env = elasticbeanstalk.get_environment(env_name=env_name)
    cloudformation_stack_name = 'awseb-' + env.id + '-stack'
    cloudformation.wait_until_stack_exists(cloudformation_stack_name)
    template = cloudformation.get_template(cloudformation_stack_name)

    url = None
    try:
        url = template['TemplateBody']['Parameters']['AppSource']['Default']
    except KeyError:
        io.log_warning('{}. '.format(strings['cloudformation.cannot_find_app_source_for_environment']))

    return url
Esempio n. 34
0
def validate_build_config(build_config):
    if build_config.service_role is not None:
        # Verify that the service role exists in the customers account
        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']

        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.")
        # Fail because the service role is required
    if build_config.image is None:
        #  Fail because the image is required
        raise ValidationError("No image specified in buildspec; this is a required argument.")
Esempio n. 35
0
    def do_command(self):
        # get arguments
        self.interactive = self.app.pargs.interactive
        self.region = self.app.pargs.region
        self.noverify = self.app.pargs.no_verify_ssl
        self.force_non_interactive = False

        # Determine if the customer is avoiding interactive mode by setting the platform flag
        if self.app.pargs.platform:
            self.force_non_interactive = True

        # Code Commit integration
        self.source = self.app.pargs.source
        source_location = None
        branch = None
        repository = None
        if self.source is not None:
            source_location, repository, branch = utils.parse_source(self.source)

        # The user specifies directories to initialize
        self.modules = self.app.pargs.modules
        if self.modules and len(self.modules) > 0:
            self.initialize_multiple_directories()
            return

        default_env = self.get_old_values()
        fileoperations.touch_config_folder()

        if self.interactive:
            self.region = get_region(self.region, self.interactive, self.force_non_interactive)
        else:
            self.region = get_region_from_inputs(self.region)
        aws.set_region(self.region)

        self.region = set_up_credentials(self.app.pargs.profile, self.region, self.interactive)

        self.solution = self.get_solution_stack()
        self.app_name = self.get_app_name()
        if self.noverify:
            fileoperations.write_config_setting('global',
                                                'no-verify-ssl', True)

        if not default_env and not self.interactive:
            # try to get default env from config file if exists
            try:
                default_env = commonops.get_current_branch_environment()
            except NotInitializedError:
                default_env = None
        elif self.interactive:
            default_env = None

        if self.force_non_interactive:
            default_env = '/ni'

        # Create application
        sstack, key = commonops.pull_down_app_info(self.app_name, default_env=default_env) if elasticbeanstalk.application_exist(self.app_name) \
            else commonops.create_app(self.app_name, default_env=default_env)

        if not self.solution:
            self.solution = sstack

        platform_set = False
        if not self.solution or \
                (self.interactive and not self.app.pargs.platform):
            if fileoperations.env_yaml_exists():
                env_yaml_platform = fileoperations.get_platform_from_env_yaml()
                if env_yaml_platform:
                    platform = solutionstack.SolutionStack(env_yaml_platform).platform_shorthand
                    self.solution = platform
                    platform_set = True

            if not platform_set:
                self.solution = solution_stack_ops.get_solution_stack_from_customer().platform_shorthand

        # Select CodeBuild image if BuildSpec is present do not prompt or show if we are non-interactive
        if fileoperations.build_spec_exists() and not self.force_non_interactive:
            build_spec = fileoperations.get_build_configuration()
            if build_spec is not None and build_spec.image is None:
                LOG.debug("Buildspec file is present but image is does not exist. Attempting to fill best guess.")
                platform_image = initializeops.get_codebuild_image_from_platform(self.solution)

                # If the return is a dictionary then it must be a single image and we can use that automatically
                if type(platform_image) is dict:
                    io.echo('codebuild.latestplatform'.replace('{platform}', self.solution))
                else:
                    # Otherwise we have an array for images which we must prompt the customer to pick from
                    io.echo(prompts['codebuild.getplatform'].replace('{platform}', self.solution))
                    selected = utils.prompt_for_index_in_list(map(lambda image: image['description'], platform_image))
                    platform_image = platform_image[selected]
                    platform_image['name'] = utils.decode_bytes(platform_image['name'])

                # Finally write the CodeBuild image back to the buildspec file
                fileoperations.write_config_setting(fileoperations.buildspec_config_header,
                                                    'Image',
                                                    platform_image['name'],
                                                    file=fileoperations.buildspec_name)

        # Setup code commit integration
        # Ensure that git is setup
        source_control = SourceControl.get_source_control()
        try:
            source_control_setup = source_control.is_setup()
            if source_control_setup is None:
                source_control_setup = False
        except CommandError:
            source_control_setup = False

        default_branch_exists = False
        if gitops.git_management_enabled() and not self.interactive:
            default_branch_exists = True

        # Warn the customer if they picked a region that CodeCommit is not supported
        codecommit_region_supported = codecommit.region_supported(self.region)

        if self.source is not None and not codecommit_region_supported:
            io.log_warning(strings['codecommit.badregion'])

        # Prompt customer to opt into CodeCommit unless one of the follows holds:
        if self.force_non_interactive:
            prompt_codecommit = False
        elif not codecommit.region_supported(self.region):
            prompt_codecommit = False
        elif self.source and source_location.lower() != 'codecommit':
            # Do not prompt if customer has already specified a code source to
            # associate the EB workspace with
            prompt_codecommit = False
        elif default_branch_exists:
            # Do not prompt if customer has already configured the EB application
            # in the present working directory with Git
            prompt_codecommit = False
        else:
            prompt_codecommit = True

        # Prompt for interactive CodeCommit
        if prompt_codecommit:
            if not source_control_setup:
                io.echo(strings['codecommit.nosc'])
            else:
                io.echo(strings['codecommit.ccwarning'])
                try:
                    if not self.source:
                        io.validate_action(prompts['codecommit.usecc'], "y")

                    # Setup git config settings for code commit credentials
                    source_control.setup_codecommit_cred_config()

                    # Get user specified repository
                    remote_url = None
                    if repository is None:
                        repository = get_repository_interactive()
                    else:
                        try:
                            setup_codecommit_remote_repo(repository, source_control)
                        except ServiceError as ex:
                            if self.source:
                                create_codecommit_repository(repository)
                                setup_codecommit_remote_repo(repository, source_control)
                            else:
                                io.log_error(strings['codecommit.norepo'])
                                raise ex

                    # Get user specified branch
                    if branch is None:
                        branch = get_branch_interactive(repository)
                    else:
                        try:
                            codecommit.get_branch(repository, branch)
                        except ServiceError as ex:
                            if self.source:
                                create_codecommit_branch(source_control, branch)
                            else:
                                io.log_error(strings['codecommit.nobranch'])
                                raise ex
                        source_control.setup_existing_codecommit_branch(branch, remote_url)

                except ValidationError:
                    LOG.debug("Denied option to use CodeCommit, continuing initialization")

        # Initialize the whole setup
        initializeops.setup(self.app_name, self.region, self.solution, dir_path=None, repository=repository, branch=branch)

        if 'IIS' not in self.solution:
            self.keyname = self.get_keyname(default=key)

            if self.keyname == -1:
                self.keyname = None

            fileoperations.write_config_setting('global', 'default_ec2_keyname',
                                                self.keyname)

        # Default to including git submodules when creating zip files through `eb create`/`eb deploy`.
        fileoperations.write_config_setting('global', 'include_git_submodules', True)
Esempio n. 36
0
def create_platform_version(
        version,
        major_increment,
        minor_increment,
        patch_increment,
        instance_type,
        vpc = None,
        staged=False,
        timeout=None):

    platform_name = fileoperations.get_platform_name()
    instance_profile = fileoperations.get_instance_profile(None)
    key_name = commonops.get_default_keyname()

    if version is None:
        version = _get_latest_version(platform_name=platform_name, owner=Constants.OWNED_BY_SELF, ignored_states=[])

        if version is None:
            version = '1.0.0'
        else:
            major, minor, patch = version.split('.', 3)

            if major_increment:
                major = str(int(major) + 1)
                minor = '0'
                patch = '0'
            if minor_increment:
                minor = str(int(minor) + 1)
                patch = '0'
            if patch_increment or not(major_increment or minor_increment):
                patch = str(int(patch) + 1)

            version = "%s.%s.%s" % (major, minor, patch)

    if not VALID_PLATFORM_VERSION_FORMAT.match(version):
        raise InvalidPlatformVersionError(strings['exit.invalidversion'])

    cwd = os.getcwd()
    fileoperations._traverse_to_project_root()

    try:
        if heuristics.directory_is_empty():
            raise PlatformWorkspaceEmptyError(strings['exit.platformworkspaceempty'])
    finally:
        os.chdir(cwd)

    if not heuristics.has_platform_definition_file():
        raise PlatformWorkspaceEmptyError(strings['exit.no_pdf_file'])

    source_control = SourceControl.get_source_control()
    if source_control.untracked_changes_exist():
        io.log_warning(strings['sc.unstagedchanges'])

    version_label = source_control.get_version_label()
    if staged:
        # Make a unique version label
        timestamp = datetime.now().strftime("%y%m%d_%H%M%S")
        version_label = version_label + '-stage-' + timestamp

    file_descriptor, original_platform_yaml = tempfile.mkstemp()
    os.close(file_descriptor)

    copyfile('platform.yaml', original_platform_yaml)

    s3_bucket = None
    s3_key = None

    try:
        # Add option settings to platform.yaml
        _enable_healthd()

        s3_bucket, s3_key = get_app_version_s3_location(platform_name, version_label)

        # Create zip file if the application version doesn't exist
        if s3_bucket is None and s3_key is None:
            file_name, file_path = _zip_up_project(version_label, source_control, staged=staged)
        else:
            file_name = None
            file_path = None
    finally:
        # Restore original platform.yaml
        move(original_platform_yaml, 'platform.yaml')

    # Use existing bucket if it exists
    bucket = elasticbeanstalk.get_storage_location() if s3_bucket is None else s3_bucket

    # Use existing key if it exists
    key = platform_name + '/' + file_name if s3_key is None else s3_key

    try:
        s3.get_object_info(bucket, key)
        io.log_info('S3 Object already exists. Skipping upload.')
    except NotFoundError:
        io.log_info('Uploading archive to s3 location: ' + key)
        s3.upload_platform_version(bucket, key, file_path)

    # Just deletes the local zip
    fileoperations.delete_app_versions()
    io.log_info('Creating Platform Version ' + version_label)
    response = elasticbeanstalk.create_platform_version(
        platform_name, version, bucket, key, instance_profile, key_name, instance_type, vpc)


    # TODO: Enable this once the API returns the name of the environment associated with a
    # CreatePlatformRequest, and remove hard coded value. There is currently only one type
    # of platform builder, we may support additional builders in the future.
    #environment_name = response['PlatformSummary']['EnvironmentName']
    environment_name = 'eb-custom-platform-builder-packer'

    io.echo(colored(
        strings['platformbuildercreation.info'].format(environment_name), attrs=['reverse']))

    fileoperations.update_platform_version(version)
    commonops.set_environment_for_current_branch(environment_name)

    arn = response['PlatformSummary']['PlatformArn']
    request_id = response['ResponseMetadata']['RequestId']

    if not timeout:
        timeout = 30

    # Share streamer for platform events and builder events
    streamer = io.get_event_streamer()

    builder_events = threading.Thread(
        target=logsops.stream_platform_logs,
        args=(platform_name, version, streamer, 5, None, PackerStreamFormatter()))
    builder_events.daemon = True

    # Watch events from builder logs
    builder_events.start()
    commonops.wait_for_success_events(
        request_id,
        platform_arn=arn,
        streamer=streamer,
        timeout_in_minutes=timeout
    )