Esempio n. 1
0
    def execute(self):
        """Execute all checks and preparations."""
        log(_green("STATE: Started"), self._log_file)

        online_app, offline_app = get_blue_green_apps(self._app, self._db.apps,
                                                      self._log_file)
        if not online_app:
            self._worker.update_status(
                "aborted",
                message=self._get_notification_message_aborted(
                    self._app,
                    "Blue/green is not enabled on this app or not well configured"
                ))
            return

        copy_ami_option = (self._job['options'][0] if 'options' in self._job
                           and len(self._job['options']) > 0 else
                           get_blue_green_copy_ami_config(self._config))
        copy_ami_option = boolify(copy_ami_option)

        app_region = self._app['region']
        as_conn3 = self._cloud_connection.get_connection(app_region,
                                                         ['autoscaling'],
                                                         boto_version='boto3')

        as_group, as_group_processes_to_suspend = get_autoscaling_group_and_processes_to_suspend(
            as_conn3, offline_app, self._log_file)
        suspend_autoscaling_group_processes(as_conn3, as_group,
                                            as_group_processes_to_suspend,
                                            self._log_file)

        try:
            lb_mgr = load_balancing.get_lb_manager(
                self._cloud_connection, self._app['region'],
                online_app["safe-deployment"]["load_balancer_type"])

            # check if app is online
            if not online_app:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        self._app,
                        "Blue/green is not enabled on this app or not well configured"
                    ))
                return

            running_jobs = get_running_jobs(self._db, online_app['_id'],
                                            offline_app['_id'],
                                            self._job['_id'])
            if abort_if_other_bluegreen_job(
                    running_jobs, self._worker,
                    self._get_notification_message_aborted(
                        self._app,
                        "Please wait until the end of the current jobs before triggering a Blue/green operation"
                    ), self._log_file):
                return

            # Check if app has up to date AMI
            if ((not copy_ami_option and 'ami' not in offline_app)
                    or (copy_ami_option and 'ami' not in online_app)):
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        offline_app,
                        "Please run `Buildimage` first or use the `copy_ami` option"
                    ))
                return

            # Check if app has AS
            if offline_app['autoscale']['name'] and online_app['autoscale'][
                    'name']:
                if not (check_autoscale_exists(
                        self._cloud_connection,
                        offline_app['autoscale']['name'],
                        offline_app['region']) and check_autoscale_exists(
                            self._cloud_connection,
                            online_app['autoscale']['name'],
                            online_app['region'])):
                    self._worker.update_status(
                        "aborted",
                        message=self._get_notification_message_aborted(
                            offline_app,
                            "Please check that the configured AutoScale on both green and blue app exists."
                        ))
                    return
            else:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        offline_app,
                        "Please set an AutoScale on both green and blue app."))
                return

            # Check if we have two different AS !
            if offline_app['autoscale']['name'] == online_app['autoscale'][
                    'name']:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        offline_app,
                        "Please set a different AutoScale on green and blue app."
                    ))
                return

            if copy_ami_option:
                log(
                    "Copy AMI option activated. AMI used by [{0}] will be reused by [{1}]"
                    .format(online_app['autoscale']['name'],
                            offline_app['autoscale']['name']), self._log_file)

            # Check if modules have been deployed
            if get_blue_green_config(self._config, 'preparebluegreen',
                                     'module_deploy_required', False):
                if not check_app_manifest(
                        offline_app, self._config, self._log_file,
                        get_path_from_app_with_color(offline_app)):
                    self._worker.update_status(
                        "aborted",
                        message=self._get_notification_message_aborted(
                            offline_app, "Please deploy your app's modules"))
                    return

            # Check if instances are already running
            if get_instances_from_autoscaling(offline_app['autoscale']['name'],
                                              as_conn3):
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        offline_app,
                        "Autoscaling Group of offline app should be empty."))
                return

            # Get the online ELB
            online_elbs = lb_mgr.list_lbs_from_autoscale(
                online_app['autoscale']['name'], self._log_file)
            if len(online_elbs) == 0:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        offline_app,
                        "Online app AutoScale is not attached to a valid Elastic Load Balancer"
                    ))
                return

            # Create the temporary ELB: ghost-bluegreentemp-{original ELB name}, duplicated from the online ELB
            temp_elb_name, new_elb_dns = (None, None)
            create_temporary_elb_option = (
                self._job['options'][1]
                if 'options' in self._job and len(self._job['options']) > 1
                else get_blue_green_create_temporary_elb_config(self._config))
            if boolify(create_temporary_elb_option):
                online_elb = online_elbs[0]
                temp_elb_name = "bgtmp-{0}".format(
                    offline_app['_id'])[:31]  # ELB name is 32 char long max
                log(
                    _green(
                        "Creating the temporary ELB [{0}] by copying parameters from [{1}]"
                        .format(temp_elb_name, online_elb)), self._log_file)
                new_elb_dns = lb_mgr.copy_lb(
                    temp_elb_name, online_elb, {
                        'app_id': str(offline_app['_id']),
                        'bluegreen-temporary': 'true'
                    }, self._log_file)

                # Register the temporary ELB into the AutoScale
                log(
                    _green("Attaching ELB [{0}] to the AutoScale [{1}]".format(
                        temp_elb_name, offline_app['autoscale']['name'])),
                    self._log_file)
                lb_mgr.register_lbs_into_autoscale(
                    offline_app['autoscale']['name'], [], [temp_elb_name],
                    self._log_file)

            offline_app['autoscale']['min'] = online_app['autoscale']['min']
            offline_app['autoscale']['max'] = online_app['autoscale']['max']
            if copy_ami_option:
                offline_app['ami'] = online_app['ami']
                offline_app['build_infos']['ami_name'] = online_app[
                    'build_infos']['ami_name']
                log(
                    "Copying AMI [{0}]({1}) into offline app [{2}]".format(
                        offline_app['ami'],
                        offline_app['build_infos']['ami_name'],
                        str(offline_app['_id'])), self._log_file)
                self._update_app_ami(offline_app)
            # Update AutoScale properties in DB App
            self._update_app_autoscale_options(offline_app, online_app,
                                               self._log_file)

            # Update AutoScale properties and starts instances
            if copy_ami_option:
                try:
                    if not create_userdata_launchconfig_update_asg(
                            offline_app['ami'],
                            self._cloud_connection,
                            offline_app,
                            self._config,
                            self._log_file,
                            update_as_params=True):
                        self._worker.update_status(
                            "failed",
                            message=self._get_notification_message_failed(
                                online_app, offline_app, ""))
                        return
                except:
                    traceback.print_exc(self._log_file)
                    self._worker.update_status(
                        "failed",
                        message=self._get_notification_message_failed(
                            online_app, offline_app, ""))
                    return
            else:
                update_auto_scale(self._cloud_connection,
                                  offline_app,
                                  None,
                                  self._log_file,
                                  update_as_params=True)

            log(
                _green(
                    "Starting at least [{0}] instance(s) into the AutoScale [{1}]"
                    .format(offline_app['autoscale']['min'],
                            offline_app['autoscale']['name'])), self._log_file)

            self._worker.update_status(
                "done",
                message=self._get_notification_message_done(
                    offline_app, temp_elb_name, new_elb_dns))
        except GCallException as e:
            self._worker.update_status(
                "failed",
                message=self._get_notification_message_failed(
                    online_app, offline_app, e))
        finally:
            resume_autoscaling_group_processes(as_conn3, as_group,
                                               as_group_processes_to_suspend,
                                               self._log_file)
Esempio n. 2
0
    def execute(self):
        log(_green("STATE: Started"), self._log_file)

        online_app, offline_app = get_blue_green_apps(self._app,
                                                      self._worker._db.apps,
                                                      self._log_file)
        if not offline_app:
            self._worker.update_status(
                "aborted",
                message=self._get_notification_message_aborted(
                    self._app,
                    "Blue/green is not enabled on this app or not well configured"
                ))
            return

        running_jobs = get_running_jobs(self._db, online_app['_id'],
                                        offline_app['_id'], self._job['_id'])
        if abort_if_other_bluegreen_job(
                running_jobs, self._worker,
                self._get_notification_message_aborted(
                    self._app,
                    "Please wait until the end of the current jobs before triggering a Blue/green operation"
                ), self._log_file):
            return

        # Check ASG
        if offline_app['autoscale']['name'] and online_app['autoscale']['name']:
            if not (check_autoscale_exists(self._cloud_connection,
                                           offline_app['autoscale']['name'],
                                           offline_app['region'])
                    and check_autoscale_exists(self._cloud_connection,
                                               online_app['autoscale']['name'],
                                               online_app['region'])):
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        offline_app,
                        "Not AutoScale group found on the offline app to purge."
                    ))
                return
        else:
            self._worker.update_status(
                "aborted",
                message=self._get_notification_message_aborted(
                    offline_app,
                    "Not AutoScale group found on the offline app to purge."))
            return

        # Check if we have two different AS !
        if offline_app['autoscale']['name'] == online_app['autoscale']['name']:
            self._worker.update_status(
                "aborted",
                message=self._get_notification_message_aborted(
                    offline_app,
                    "Please set a different AutoScale on green and blue app."))
            return

        # Retrieve autoscaling infos, if any
        app_region = offline_app['region']
        as_conn3 = self._cloud_connection.get_connection(app_region,
                                                         ['autoscaling'],
                                                         boto_version='boto3')
        as_group, as_group_processes_to_suspend = get_autoscaling_group_and_processes_to_suspend(
            as_conn3, offline_app, self._log_file)
        suspend_autoscaling_group_processes(as_conn3, as_group,
                                            as_group_processes_to_suspend,
                                            self._log_file)

        try:
            lb_mgr = load_balancing.get_lb_manager(
                self._cloud_connection, self._app['region'],
                online_app["safe-deployment"]["load_balancer_type"])

            # Check if instances are running
            if not get_instances_from_autoscaling(
                    offline_app['autoscale']['name'], as_conn3):
                log(
                    _yellow(
                        " WARNING: Autoscaling Group [{0}] of offline app is empty. "
                        "No running instances to clean detected.".format(
                            offline_app['autoscale']['name'])), self._log_file)

            temp_elb_names = lb_mgr.list_lbs_from_autoscale(
                offline_app['autoscale']['name'], self._log_file,
                {'bluegreen-temporary': 'true'})

            if len(temp_elb_names) > 1:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        offline_app,
                        "There are more than one temporary ELB associated to the ASG '{0}' \n"
                        "ELB found: {1}".format(
                            offline_app['autoscale']['name'],
                            str(temp_elb_names))))
                return

            # Detach temp ELB from ASG
            log(
                _green(
                    "Detach the current temporary ELB [{0}] from the AutoScale [{1}]"
                    .format(temp_elb_names, offline_app['autoscale']['name'])),
                self._log_file)
            lb_mgr.register_lbs_into_autoscale(
                offline_app['autoscale']['name'], temp_elb_names, None,
                self._log_file)

            # Update ASG and kill instances
            log("Update AutoScale with `0` on mix, max, desired values.",
                self._log_file)
            log(
                _yellow(
                    "Destroy all instances in the AutoScale and all instances matching the `app_id` [{0}]"
                    .format(offline_app['_id'])), self._log_file)
            flush_instances_update_autoscale(as_conn3, self._cloud_connection,
                                             offline_app, self._log_file)

            # Destroy temp ELB
            if temp_elb_names:
                lb_mgr.destroy_lb(temp_elb_names[0], self._log_file)
            else:
                log(" INFO: No ELB to destroy", self._log_file)

            # Update App Autoscale values, next buildimage or updateautoscaling should not set values different from 0
            self._update_app_autoscale_options(offline_app, self._log_file)

            # All good
            self._worker.update_status(
                "done",
                message=self._get_notification_message_done(offline_app))
        except GCallException as e:
            self._worker.update_status(
                "failed",
                message=self._get_notification_message_failed(
                    offline_app, str(e)))
        finally:
            # Resume autoscaling groups in any case
            resume_autoscaling_group_processes(as_conn3, as_group,
                                               as_group_processes_to_suspend,
                                               self._log_file)
Esempio n. 3
0
    def execute(self):
        log(_green("STATE: Started"), self._log_file)
        swap_execution_strategy = (
            self._job['options'][0] if 'options' in self._job
            and len(self._job['options']) > 0 else "isolated")
        online_app, to_deploy_app = get_blue_green_apps(
            self._app, self._worker._db.apps, self._log_file)
        if not online_app:
            self._worker.update_status(
                "aborted",
                message=self._get_notification_message_aborted(
                    self._app,
                    "Blue/green is not enabled on this app or not well configured"
                ))
            return

        running_jobs = get_running_jobs(self._db, online_app['_id'],
                                        to_deploy_app['_id'], self._job['_id'])
        if abort_if_other_bluegreen_job(
                running_jobs, self._worker,
                self._get_notification_message_aborted(
                    self._app,
                    "Please wait until the end of the current jobs before triggering a Blue/green operation"
                ), self._log_file):
            return

        try:
            lb_mgr = load_balancing.get_lb_manager(
                self._cloud_connection, self._app['region'],
                online_app["safe-deployment"]["load_balancer_type"])

            # Check AMI
            if 'ami' not in to_deploy_app:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        to_deploy_app, "Please run `Buildimage` first"))
                return
            # Check if modules have been deployed
            if not check_app_manifest(
                    to_deploy_app, self._config, self._log_file,
                    get_path_from_app_with_color(to_deploy_app)):
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        to_deploy_app, "Please deploy your app's modules"))
                return
            # Check ASG
            if to_deploy_app['autoscale']['name'] and online_app['autoscale'][
                    'name']:
                if not (check_autoscale_exists(
                        self._cloud_connection,
                        to_deploy_app['autoscale']['name'],
                        to_deploy_app['region']) and check_autoscale_exists(
                            self._cloud_connection,
                            online_app['autoscale']['name'],
                            online_app['region'])):
                    self._worker.update_status(
                        "aborted",
                        message=self._get_notification_message_aborted(
                            to_deploy_app,
                            "Please set an AutoScale on both green and blue app"
                        ))
                    return
            else:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        to_deploy_app,
                        "Please set an AutoScale on both green and blue app."))
                return

            # Check if we have two different AS !
            if to_deploy_app['autoscale']['name'] == online_app['autoscale'][
                    'name']:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        to_deploy_app,
                        "Please set a different AutoScale on green and blue app."
                    ))
                return

            # Check if we're ready to swap. If an instance is out of service
            # into the ELB pool raise an exception
            elb_instances = lb_mgr.get_instances_status_from_autoscale(
                to_deploy_app['autoscale']['name'], self._log_file)
            if len(elb_instances) == 0:
                self._worker.update_status(
                    "aborted",
                    message=self._get_notification_message_aborted(
                        to_deploy_app,
                        "The offline application [{0}] doesn't have a valid Load Balancer associated.'"
                        .format(to_deploy_app['_id'])))
                return
            for e in elb_instances.values():
                if len(e.values()) == 0:
                    self._worker.update_status(
                        "aborted",
                        message=self._get_notification_message_aborted(
                            to_deploy_app,
                            "An ELB of the offline application [{0}] has no instances associated.'"
                            .format(to_deploy_app['_id'])))
                    return

            if len([
                    i for i in elb_instances.values()
                    if 'outofservice' in i.values()
            ]):
                raise GCallException(
                    'Cannot continue because one or more instances are in the out of service state in the temp ELB'
                )
            else:
                log(
                    _green(
                        "AutoScale blue [{0}] and green [{1}] ready for swap".
                        format(online_app['autoscale']['name'],
                               to_deploy_app['autoscale']['name'])),
                    self._log_file)

            self._execute_swap_hook(
                online_app, to_deploy_app, 'pre_swap',
                'Pre swap script for current {status} application',
                self._log_file)

            # Swap !
            elb_name, elb_dns = self._swap_asg(lb_mgr, swap_execution_strategy,
                                               online_app, to_deploy_app,
                                               self._log_file)
            if not elb_name:
                self._worker.update_status(
                    "failed",
                    message=self._get_notification_message_failed(
                        online_app, to_deploy_app,
                        'Unable to make blue-green swap'))
                return

            self._execute_swap_hook(
                online_app, to_deploy_app, 'post_swap',
                'Post swap script for previously {status} application',
                self._log_file)

            # All good
            done_notif = self._get_notification_message_done(
                online_app, online_app['autoscale']['name'],
                to_deploy_app['autoscale']['name'], elb_name, elb_dns)
            self._worker.update_status("done", message=done_notif)
        except GCallException as e:
            self._worker.update_status(
                "failed",
                message=self._get_notification_message_failed(
                    online_app, to_deploy_app, str(e)))