예제 #1
0
    def deployment(self, safe_deployment_strategy):
        """ Main entry point for Host Deployment Manager process

            :type safe_deployment_strategy: string/enum
            :return True if operation succeed otherwise an Exception will be raised.
        """

        app_name = self._app['name']
        app_env = self._app['env']
        app_role = self._app['role']
        app_region = self._app['region']
        app_blue_green, app_color = get_blue_green_from_app(self._app)

        # Retrieve autoscaling infos, if any
        as_conn = 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_conn, self._app,
                                                                                                 self._log_file)
        try:
            # Suspend autoscaling
            suspend_autoscaling_group_processes(as_conn, as_group, as_group_processes_to_suspend, self._log_file)
            # Wait for pending instances to become ready
            while True:
                pending_instances = find_ec2_pending_instances(self._cloud_connection, app_name, app_env, app_role,
                                                               app_region, as_group, ghost_color=app_color)
                if not pending_instances:
                    break
                log(
                    "INFO: waiting 10s for {} instance(s) to become running before proceeding with deployment: {}".format(
                        len(pending_instances), pending_instances), self._log_file)
                time.sleep(10)
            running_instances = find_ec2_running_instances(self._cloud_connection, app_name, app_env, app_role,
                                                           app_region, ghost_color=app_color)
            if running_instances:
                if safe_deployment_strategy and self._safe_infos:
                    self._as_name = as_group
                    self._hosts_list = running_instances
                    return self.safe_manager(safe_deployment_strategy)
                else:
                    self._hosts_list = [host['private_ip_address'] for host in running_instances]
                    self.trigger_launch(self._hosts_list)
                    return True
            else:
                raise GCallException(
                    "No instance found in region {region} with tags app:{app}, env:{env}, role:{role}{color}".format(
                        region=app_region,
                        app=app_name,
                        env=app_env,
                        role=app_role,
                        color=', color:%s' % app_color if app_color else ''))
        finally:
            resume_autoscaling_group_processes(as_conn, as_group, as_group_processes_to_suspend, self._log_file)
예제 #2
0
    def do_rolling(self, rolling_strategy):
        """  Main entry point for Rolling Update process.

            :param  rolling_strategy string: The type of rolling strategy(1by1-1/3-25%-50%)
            :return True if operation succeed otherwise an Exception will be raised.
        """
        hosts = split_hosts_list(
            self.hosts_list,
            rolling_strategy) if rolling_strategy else [self.hosts_list]
        for host_group in hosts:
            if self.safe_infos['load_balancer_type'] == 'elb':
                self.elb_rolling_update(host_group)
#            elif self.safe_infos['load_balancer_type'] == 'alb':
            else:
                raise GCallException(
                    'Load balancer type not supported for Rolling update option'
                )
            log('Waiting 10s before going on next instance group',
                self.log_file)
            time.sleep(10)
        return True
예제 #3
0
    def alb_safe_deployment(self, instances_list):
        """ Manage the safe deployment process for the Application Load Balancer.

            :param  instances_list: list: Instances on which to
                    (list of dict. ex: [{'id':XXX, 'private_ip_address':XXXX}...]).
            :return True if operation successed or raise an Exception.
        """
        if not self._as_name:
            raise GCallException('Cannot continue because there is no AuoScaling Group configured')

        app_region = self._app['region']

        alb_mgr = load_balancing.get_lb_manager(self._cloud_connection, app_region, load_balancing.LB_TYPE_AWS_ALB)

        alb_targets = alb_mgr.get_instances_status_from_autoscale(self._as_name, self._log_file)

        if not len(alb_targets):
            raise GCallException('Cannot continue because there is no ALB configured in the AutoScaling Group')
        elif len([i for i in alb_targets.values() if 'unhealthy' in i.values()]):
            raise GCallException('Cannot continue because one or more instances are in the unhealthy state')
        else:
            alb_mgr.deregister_instances_from_lbs(self._as_name,
                                                  [host['id'] for host in instances_list],
                                                  self._log_file)
            wait_before_deploy = int(alb_mgr.get_lbs_max_connection_draining_value(self._as_name)) + int(
                self._safe_infos['wait_before_deploy'])
            log('Waiting {0}s: The deregistation delay time plus the custom value set for wait_before_deploy'.format(
                wait_before_deploy), self._log_file)
            time.sleep(wait_before_deploy)

            host_list = [host['private_ip_address'] for host in instances_list]
            self.trigger_launch(host_list)

            log('Waiting {0}s: The value set for wait_after_deploy'.format(self._safe_infos['wait_after_deploy']),
                self._log_file)
            time.sleep(int(self._safe_infos['wait_after_deploy']))
            alb_mgr.register_instances_from_lbs(self._as_name,
                                                [host['id'] for host in instances_list],
                                                self._log_file)
            while len([i for i in alb_mgr.get_instances_status_from_autoscale(self._as_name, self._log_file).values() if
                       'unhealthy' in i.values()]):
                log('Waiting 10s because the instance is unhealthy in the ALB', self._log_file)
                time.sleep(10)
            log('Instances: {0} have been deployed and are registered in their ALB'.format(
                str([host['private_ip_address'] for host in instances_list])), self._log_file)
            return True
예제 #4
0
    def haproxy_safe_deployment(self, instances_list):
        """ Manage the safe deployment process for the Haproxy.

            :param  instances_list:  list: Instances on which to deploy
                    (list of dict. ex: [{'id':XXX, 'private_ip_address':XXXX}...]).
            :return True if operation successed or raise an Exception.
        """
        lb_infos = [host['private_ip_address'] for host in find_ec2_running_instances(self._cloud_connection,
                                                                                      self._safe_infos['app_tag_value'],
                                                                                      self._app['env'], 'loadbalancer',
                                                                                      self._app['region'])]
        if lb_infos:
            hapi = haproxy.Haproxyapi(lb_infos, self._log_file, self._safe_infos['api_port'])
            ha_urls = hapi.get_haproxy_urls()
            if not self.haproxy_configuration_validation(hapi, ha_urls, self._safe_infos['ha_backend']):
                raise GCallException('Cannot initialize the safe deployment process because there are differences in the Haproxy \
                                      configuration files between the instances: {0}'.format(lb_infos))
            if not hapi.change_instance_state('disableserver', self._safe_infos['ha_backend'],
                                              [host['private_ip_address'] for host in instances_list]):
                raise GCallException(
                    'Cannot disable some instances: {0} in {1}. Deployment aborted'.format(instances_list, lb_infos))
            log('Waiting {0}s: The value set for wait_before_deploy'.format(self._safe_infos['wait_before_deploy']),
                self._log_file)
            time.sleep(int(self._safe_infos['wait_before_deploy']))

            host_list = [host['private_ip_address'] for host in instances_list]
            self.trigger_launch(host_list)

            log('Waiting {0}s: The value set for wait_after_deploy'.format(self._safe_infos['wait_after_deploy']),
                self._log_file)
            time.sleep(int(self._safe_infos['wait_after_deploy']))
            if not hapi.change_instance_state('enableserver', self._safe_infos['ha_backend'],
                                              [host['private_ip_address'] for host in instances_list]):
                raise GCallException(
                    'Cannot enabled some instances: {0} in {1}. Deployment aborted'.format(instances_list, lb_infos))
            # Add a sleep to let the time to pass the health check process
            time.sleep(5)
            if not self.haproxy_configuration_validation(hapi, ha_urls, self._safe_infos['ha_backend']):
                raise GCallException('Error in the post safe deployment process because there are differences in the Haproxy \
                                    configuration files between the instances: {0}. Instances: {1} have been deployed but not well enabled'.format(
                    lb_infos, instances_list))
            if not hapi.check_all_instances_up(self._safe_infos['ha_backend'], hapi.get_haproxy_conf(ha_urls[0], True)):
                raise GCallException(
                    'Error in the post safe deployment process because some instances are disable or down in the Haproxy: {0}.'.format(
                        lb_infos, instances_list))
            log('Instances: {0} have been deployed and are registered in their Haproxy'.format(str(instances_list)),
                self._log_file)
            return True
        else:
            raise GCallException('Cannot continue because no Haproxy found with the parameters: app_tag_value: {0}, app_env: {1}, app_role: loadbalancer,\
                                 app_region: {2}'.format(self._safe_infos['app_tag_value'], self._app['env'],
                                                         self._app['region']))
예제 #5
0
    def elb_rolling_update(self, instances_list):
        """ Manage the safe destroy process for the ELB.

            :param  instances_list  list: Instances on which to destroy (list of dict. ex: [{'id':XXX, 'private_ip_address':XXXX}...]).
            :return                True if operation successed or raise an Exception.
        """
        if not self.as_name:
            raise GCallException(
                'Cannot continue because there is no AutoScaling Group configured'
            )

        app_region = self.app['region']

        as_conn = self.cloud_connection.get_connection(app_region,
                                                       ['autoscaling'],
                                                       boto_version='boto3')
        lb_mgr = load_balancing.get_lb_manager(self.cloud_connection,
                                               app_region,
                                               load_balancing.LB_TYPE_AWS_CLB)
        destroy_asg_policy = ['OldestLaunchConfiguration']

        try:
            elb_instances = lb_mgr.get_instances_status_from_autoscale(
                self.as_name, self.log_file)
            asg_infos = get_autoscaling_group_object(as_conn, self.as_name)
            if not len(elb_instances):
                raise GCallException(
                    'Cannot continue because there is no ELB configured in the AutoScaling Group'
                )
            elif 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'
                )
            elif not check_autoscale_instances_lifecycle_state(
                    asg_infos['Instances']):
                raise GCallException(
                    'Cannot continue because one or more instances are not in InService Lifecycle state'
                )
            else:
                group_size = len(instances_list)
                original_termination_policies = asg_infos[
                    'TerminationPolicies']

                log(
                    _green(
                        'Suspending "Terminate" process in the AutoScale and provisioning %s instance(s)'
                        % group_size), self.log_file)
                suspend_autoscaling_group_processes(as_conn, self.as_name,
                                                    ['Terminate'],
                                                    self.log_file)
                update_auto_scaling_group_attributes(
                    as_conn, self.as_name, asg_infos['MinSize'],
                    asg_infos['MaxSize'] + group_size,
                    asg_infos['DesiredCapacity'] + group_size)

                log(
                    _green(
                        'Deregister old instances from the Load Balancer (%s)'
                        % str([host['id'] for host in instances_list])),
                    self.log_file)
                lb_mgr.deregister_instances_from_lbs(
                    elb_instances.keys(),
                    [host['id'] for host in instances_list], self.log_file)
                wait_con_draining = int(
                    lb_mgr.get_lbs_max_connection_draining_value(
                        elb_instances.keys()))
                log(
                    'Waiting {0}s: The connection draining time'.format(
                        wait_con_draining), self.log_file)
                time.sleep(wait_con_draining)

                asg_updated_infos = get_autoscaling_group_object(
                    as_conn, self.as_name)
                while len(asg_updated_infos['Instances']
                          ) < asg_updated_infos['DesiredCapacity']:
                    log(
                        'Waiting 30s because the instance(s) are not provisioned in the AutoScale',
                        self.log_file)
                    time.sleep(30)
                    asg_updated_infos = get_autoscaling_group_object(
                        as_conn, self.as_name)
                while not check_autoscale_instances_lifecycle_state(
                        asg_updated_infos['Instances']):
                    log(
                        'Waiting 30s because the instance(s) are not in InService state in the AutoScale',
                        self.log_file)
                    time.sleep(30)
                    asg_updated_infos = get_autoscaling_group_object(
                        as_conn, self.as_name)

                while len([
                        i for i in lb_mgr.get_instances_status_from_autoscale(
                            self.as_name, self.log_file).values()
                        if 'outofservice' in i.values()
                ]):
                    log(
                        'Waiting 10s because the instance(s) are not in service in the ELB',
                        self.log_file)
                    time.sleep(10)

                suspend_autoscaling_group_processes(as_conn, self.as_name,
                                                    ['Launch', 'Terminate'],
                                                    self.log_file)
                log(
                    _green(
                        'Restore initial AutoScale attributes and destroy old instances for this group (%s)'
                        % str([host['id'] for host in instances_list])),
                    self.log_file)
                update_auto_scaling_group_attributes(
                    as_conn, self.as_name, asg_infos['MinSize'],
                    asg_infos['MaxSize'], asg_infos['DesiredCapacity'],
                    destroy_asg_policy)
                destroy_specific_ec2_instances(self.cloud_connection, self.app,
                                               instances_list, self.log_file)

                resume_autoscaling_group_processes(as_conn, self.as_name,
                                                   ['Terminate'],
                                                   self.log_file)
                asg_updated_infos = get_autoscaling_group_object(
                    as_conn, self.as_name)
                while len(asg_updated_infos['Instances']
                          ) > asg_updated_infos['DesiredCapacity']:
                    log(
                        'Waiting 20s because the old instance(s) are not removed from the AutoScale',
                        self.log_file)
                    time.sleep(20)
                    asg_updated_infos = get_autoscaling_group_object(
                        as_conn, self.as_name)

                update_auto_scaling_group_attributes(
                    as_conn, self.as_name, asg_infos['MinSize'],
                    asg_infos['MaxSize'], asg_infos['DesiredCapacity'],
                    original_termination_policies)
                log(
                    _green(
                        '%s instance(s) have been re-generated and are registered in their ELB'
                        % group_size), self.log_file)
                return True
        except Exception as e:
            raise
        finally:
            resume_autoscaling_group_processes(as_conn, self.as_name,
                                               ['Launch', 'Terminate'],
                                               self.log_file)