Esempio n. 1
0
    def get_diff_context(self):
        context = {
            "new_version_details": self.new_version_details,
            "user": get_default_username(),
            "LABELS_TO_EXPAND": LABELS_TO_EXPAND,
            "errors": [],
            "warnings": []
        }

        if self.deployed_commit_matches_latest_commit:
            context["errors"].append(
                "Versions are identical. No changes since last deploy.")
            return context

        if not (self.current_commit and self.deploy_commit):
            context["warnings"].append("Insufficient info to get deploy diff.")
            return context

        context["compare_url"] = self.url

        if not self.generate_diff:
            disabled_msg = "Deploy diffs disabled for this environment."
            print(color_warning(disabled_msg))
            context["warnings"].append(disabled_msg)
            return context

        if not self.repo.permissions:
            # using unauthenticated API calls, skip diff creation to avoid hitting rate limits
            print(
                color_warning(
                    "Diff generation skipped. Supply a Github token to see deploy diffs."
                ))
            context["warnings"].append("Diff omitted.")
            return context

        try:
            pr_numbers = self._get_pr_numbers()
        except GithubException as e:
            print(color_error(f"Error getting diff commits: {e}"))
            context["warnings"].append(
                "There was an error fetching the PRs since the last deploy.")
            return context

        if len(pr_numbers) > 500:
            context["warnings"].append("There are too many PRs to display.")
            return context
        elif not pr_numbers:
            context["warnings"].append("No PRs merged since last release.")
            return context

        pool = Pool(5)
        pr_infos = [_f for _f in pool.map(self._get_pr_info, pr_numbers) if _f]

        context["pr_infos"] = pr_infos
        prs_by_label = self._get_prs_by_label(pr_infos)
        context["prs_by_label"] = prs_by_label
        return context
Esempio n. 2
0
    def run(self, args, unknown_args):
        if 'destroy' in unknown_args:
            puts(color_error("Refusing to run a terraform command containing the argument 'destroy'."))
            puts(color_error("It's simply not worth the risk."))
            exit(-1)

        environment = get_environment(args.env_name)
        run_dir = environment.paths.get_env_file_path('.generated-terraform')
        modules_dir = os.path.join(TERRAFORM_DIR, 'modules')
        modules_dest = os.path.join(run_dir, 'modules')
        if not os.path.isdir(run_dir):
            os.mkdir(run_dir)
        if not os.path.isdir(run_dir):
            os.mkdir(run_dir)
        if not (os.path.exists(modules_dest) and os.readlink(modules_dest) == modules_dir):
            os.symlink(modules_dir, modules_dest)

        if args.username != get_default_username():
            print_help_message_about_the_commcare_cloud_default_username_env_var(args.username)

        key_name = args.username

        try:
            generate_terraform_entrypoint(environment, key_name, run_dir,
                                          apply_immediately=args.apply_immediately)
        except UnauthorizedUser as e:
            allowed_users = environment.users_config.dev_users.present
            puts(color_error(
                "Unauthorized user {}.\n\n"
                "Use COMMCARE_CLOUD_DEFAULT_USERNAME or --username to pass in one of the allowed ssh users:{}"
                .format(e.username, '\n  - '.join([''] + allowed_users))))
            return -1

        if not args.skip_secrets and unknown_args and unknown_args[0] in ('plan', 'apply'):
            rds_password = (
                environment.get_secret('POSTGRES_USERS.root.password')
                if environment.terraform_config.rds_instances
                else ''
            )

            with open(os.path.join(run_dir, 'secrets.auto.tfvars'), 'w', encoding='utf-8') as f:
                print('rds_password = {}'.format(json.dumps(rds_password)), file=f)

        env_vars = {'AWS_PROFILE': aws_sign_in(environment)}
        all_env_vars = os.environ.copy()
        all_env_vars.update(env_vars)
        cmd_parts = ['terraform'] + unknown_args
        cmd = ' '.join(shlex_quote(arg) for arg in cmd_parts)
        print_command('cd {}; {} {}; cd -'.format(
            run_dir,
            ' '.join('{}={}'.format(key, value) for key, value in env_vars.items()),
            cmd,
        ))
        return subprocess.call(cmd, shell=True, env=all_env_vars, cwd=run_dir)
Esempio n. 3
0
def call_record_deploy_success(environment, diff, start_time, end_time):
    delta = end_time - start_time
    args = [
        '--user',
        get_default_username(),
        '--environment',
        environment.meta_config.deploy_env,
        '--url',
        diff.url,
        '--minutes',
        str(int(delta.total_seconds() // 60)),
        '--commit',
        diff.deploy_commit,
    ]
    commcare_cloud(environment.name, 'django-manage', 'record_deploy_success',
                   *args)
Esempio n. 4
0
def record_deploy_in_datadog(environment, diff, tdelta):
    if environment.public_vars.get('DATADOG_ENABLED', False):
        print(color_summary(f">> Recording deploy in Datadog"))
        diff_url = f"\nDiff link: [Git Diff]({diff.url})"
        deploy_notification_text = (
            "Formplayer has been successfully deployed to "
            "*{}* by *{}* in *{}* minutes.\nRelease Name: {}{}".format(
                environment.name, get_default_username(),
                int(tdelta.total_seconds() / 60) or '?',
                environment.new_release_name(), diff_url))
        commcare_cloud(environment.name,
                       'send-datadog-event',
                       'Formplayer Deploy Success',
                       deploy_notification_text,
                       '--alert_type',
                       "success",
                       show_command=False)
Esempio n. 5
0
def announce_deploy_start(environment, service_name, commcare_rev=None):
    user = get_default_username()
    is_nonstandard_deploy_time = not within_maintenance_window(environment)
    is_non_default_branch = (
        commcare_rev != environment.fab_settings_config.default_branch
        and commcare_rev is not None)
    env_name = environment.meta_config.deploy_env
    subject = f"{user} has initiated a {service_name} deploy to {env_name}"
    prefix = ""
    if is_nonstandard_deploy_time:
        subject += " outside maintenance window"
        prefix = "ATTENTION: "
    if is_non_default_branch:
        subject += f" with non-default branch '{commcare_rev}'"
        prefix = "ATTENTION: "
    subject = f"{prefix}{subject}"

    send_email(
        environment,
        subject=subject,
    )
Esempio n. 6
0
def _aws_sign_in_with_iam(aws_profile,
                          duration_minutes=DEFAULT_SIGN_IN_DURATION_MINUTES,
                          force_new=False):
    """
    Create a temp session through MFA for a given aws profile

    :param aws_profile: The name of an existing aws profile to create a temp session for
    :param duration_minutes: How long to set the session expiration if a new one is created
    :param force_new: If set to True, creates new credentials even if valid ones are found
    :return: The name of temp session profile.
             (Always the passed in profile followed by ':session')
    """
    aws_session_profile = '{}:session'.format(aws_profile)
    if not force_new \
            and _has_valid_v1_session_credentials(aws_session_profile):
        return aws_session_profile

    default_username = get_default_username()
    if default_username.is_guess:
        username = input("Enter username associated with credentials [{}]: ".
                         format(default_username)) or default_username
        print_help_message_about_the_commcare_cloud_default_username_env_var(
            username)
    else:
        username = default_username
    mfa_token = input("Enter your MFA token: ")
    generate_session_profile(aws_profile, username, mfa_token,
                             duration_minutes)

    puts(color_success("✓ Sign in accepted"))
    puts(
        "You will be able to use AWS from the command line for the next {} minutes."
        .format(duration_minutes))
    puts(
        color_notice("To use this session outside of commcare-cloud, "
                     "prefix your command with AWS_PROFILE={}:session".format(
                         aws_profile)))
    return aws_session_profile
Esempio n. 7
0
class Terraform(CommandBase):
    command = 'terraform'
    help = "Run terraform for this env with the given arguments"

    arguments = (
        Argument('--skip-secrets', action='store_true', help="""
            Skip regenerating the secrets file.

            Good for not having to enter vault password again.
        """),
        Argument('--apply-immediately', action='store_true', help="""
            Apply immediately regardless fo the default.

            In RDS where the default is to apply in the next maintenance window,
            use this to apply immediately instead. This may result in a service interruption.
        """),
        Argument('--username', default=get_default_username(), help="""
            The username of the user whose public key will be put on new servers.

            Normally this would be _your_ username.
            Defaults to the value of the COMMCARE_CLOUD_DEFAULT_USERNAME environment variable
            or else the username of the user running the command.
        """),
    )

    def run(self, args, unknown_args):
        if 'destroy' in unknown_args:
            puts(color_error("Refusing to run a terraform command containing the argument 'destroy'."))
            puts(color_error("It's simply not worth the risk."))
            exit(-1)

        environment = get_environment(args.env_name)
        run_dir = environment.paths.get_env_file_path('.generated-terraform')
        modules_dir = os.path.join(TERRAFORM_DIR, 'modules')
        modules_dest = os.path.join(run_dir, 'modules')
        if not os.path.isdir(run_dir):
            os.mkdir(run_dir)
        if not os.path.isdir(run_dir):
            os.mkdir(run_dir)
        if not (os.path.exists(modules_dest) and os.readlink(modules_dest) == modules_dir):
            os.symlink(modules_dir, modules_dest)

        if args.username != get_default_username():
            print_help_message_about_the_commcare_cloud_default_username_env_var(args.username)

        key_name = args.username

        try:
            generate_terraform_entrypoint(environment, key_name, run_dir,
                                          apply_immediately=args.apply_immediately)
        except UnauthorizedUser as e:
            allowed_users = environment.users_config.dev_users.present
            puts(color_error(
                "Unauthorized user {}.\n\n"
                "Use COMMCARE_CLOUD_DEFAULT_USERNAME or --username to pass in one of the allowed ssh users:{}"
                .format(e.username, '\n  - '.join([''] + allowed_users))))
            return -1

        if not args.skip_secrets and unknown_args and unknown_args[0] in ('plan', 'apply'):
            rds_password = (
                environment.get_secret('POSTGRES_USERS.root.password')
                if environment.terraform_config.rds_instances
                else ''
            )

            with open(os.path.join(run_dir, 'secrets.auto.tfvars'), 'w', encoding='utf-8') as f:
                print('rds_password = {}'.format(json.dumps(rds_password)), file=f)

        env_vars = {'AWS_PROFILE': aws_sign_in(environment)}
        all_env_vars = os.environ.copy()
        all_env_vars.update(env_vars)
        cmd_parts = ['terraform'] + unknown_args
        cmd = ' '.join(shlex_quote(arg) for arg in cmd_parts)
        print_command('cd {}; {} {}; cd -'.format(
            run_dir,
            ' '.join('{}={}'.format(key, value) for key, value in env_vars.items()),
            cmd,
        ))
        return subprocess.call(cmd, shell=True, env=all_env_vars, cwd=run_dir)
Esempio n. 8
0
 def user(self):
     return get_default_username()