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)
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)
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)))