class RepoBuilder(): def __init__(self, task, *args, **kwargs): self.configs = kwargs.get("config") self.region_api = RegionAPI(conf=self.configs['region']) self.api = ACPAPI(conf=self.configs['region']) self.user_cs_client = UserConsoleAPI(conf=self.configs['userconsole']) self.repo_url = task['repo_url'] self.region_client = RegionBackAPI() self.tenant_id = task['tenant_id'] self.service_id = task['service_id'] self.tenant_name = task['tenant_name'] self.service_alias = task['service_alias'] self.deploy_version = task['deploy_version'] self.action = task['action'] if 'event_id' in task: self.event_id = task["event_id"] self.log = EventLog().bind(event_id=self.event_id) else: self.event_id = "" self.log = EventLog().bind(event_id=self.event_id) self.operator = task['operator'] self.build_envs = task.get('envs', {}) self.expire = task.get('expire', 60) self.start_time = int(time.time()) # self.source_dir = '/tmp/goodrain_web' self.source_dir = SOURCE_DIR.format(tenantId=self.tenant_id, serviceId=self.service_id) self.cache_dir = CACHE_DIR.format(tenantId=self.tenant_id, serviceId=self.service_id) self.tgz_dir = TGZ_DIR.format(tenantId=self.tenant_id, serviceId=self.service_id) self.build_log_dir = BUILD_LOG_DIR.format(tenantId=self.tenant_id, serviceId=self.service_id) self.build_cmd = 'plugins/scripts/build.pl' @property def build_name(self): return self.service_id[:8] + '_' + self.deploy_version @property def is_expired(self): if hasattr(self, 'expire'): current_time = int(time.time()) return bool(current_time - self.start_time > self.expire) else: return False def prepare(self): if os.path.exists(self.source_dir): shutil.rmtree(self.source_dir) for d in (self.source_dir, self.cache_dir, self.tgz_dir, self.build_log_dir): if not os.path.exists(d): os.makedirs(d) os.chown(self.tgz_dir, 200, 200) os.chown(self.cache_dir, 200, 200) def clone(self): self.log.info("开始拉取代码。。", step="build-worker") # code, output = shell.runsingle("git clone --branch master --depth 1 {0} {1}".format(self.repo_url, self.source_dir)) result = False num = 0 while num < 2: try: cmdstr = "timeout -k 9 {2} git clone {0} {1}".format( self.repo_url, self.source_dir, CLONE_TIMEOUT) if "github.com" in self.repo_url: cmdstr = "timeout -k 9 {2} git clone -c http.proxy=http://127.0.0.1:18888 {0} {1}".format( self.repo_url, self.source_dir, CLONE_TIMEOUT) shell.call(cmdstr) result = True break except shell.ExecException, e: num = num + 1 self.prepare() if num < 2: self.log.error("拉取代码发生错误,开始重试 {}".format(e.message), status="failure", step="worker-clone") else: self.log.error("拉取代码发生错误,部署停止 {}".format(e.message), status="failure", step="callback") logger.exception('build_work.main', e) result = False logger.info('build_work.main', "git clone num=" + str(num)) return result
class ImageManual(): def __init__(self, job, *args, **kwargs): self.job = job self.configs = kwargs.get("config") self.region_api = RegionAPI(conf=self.configs['region']) image_config = self.configs["publish"]["image"] self.region_client = RegionBackAPI() self.region_registry = RegistryAPI( host=image_config.get('curr_registry')) # self.region_registry.set_log_topic('mq_work.image_manual') self.oss_registry = RegistryAPI(host=image_config.get('all_registry')) self.oss_registry.set_log_topic('mq_work.image_manual') self.locker = TaskLocker(conf=self.configs['etcd']) self.api = ACPAPI(conf=self.configs['region']) self.namespace = image_config.get('oss_namespace') self.user_cs_client = UserConsoleAPI(conf=self.configs['userconsole']) def do_work(self): try: task = json.loads(self.job.body) self.task = task if "event_id" in self.task: self.event_id = task["event_id"] self.log = EventLog().bind(event_id=self.event_id, step="image_manual") else: self.event_id = "" self.log = EventLog().bind(event_id="", step="image_manual") logger.info("mq_work.image_manual", "new image_manual task: {}".format(task)) if task['action'] == 'create_new_version': self.log.info("开始升级应用。") self.create_new_version() elif task['action'] == 'download_and_deploy': self.log.info("开始下载镜像并部署应用。") self.download_and_deploy() elif task['action'] == 'delete_old_version': self.log.info("开始删除旧版本。") self.delete_old_version() except Exception as e: if self.log: self.log.error("从自定义镜像部署应用失败。{}".format(e.message), step="callback", status="failure") logger.exception('mq_work.image_manual', e) def create_new_version(self): logger.debug("mq_work.image_manual", "now create new version and upload image") def delete_old_version(self): logger.debug("mq_work.image_manual", "now delete old version") def download_and_deploy(self): image = self.task['image'] # namespace = self.task['namespace'] tenant_name = self.task['tenant_name'] service_alias = self.task['service_alias'] event_id = self.task['event_id'] service_alias = self.task.get("service_alias", None) has_download = False inner_image = self.oss_registry.rename_image(image) inner_image = "{0}_{1}".format(inner_image, service_alias) local_image = self.region_registry.rename_image(image) local_image = "{0}_{1}".format(local_image, service_alias) # 直接下载docker image try: self.log.info("开始下载镜像:{0}".format(image)) pull_result = self._pull(image) if pull_result: # image_id = self.get_image_property(image, 'Id') self._tag(image, local_image) self.log.info("修改镜像名为:{0}".format(local_image)) ok = self._push(local_image) if not ok: self.log.error("上传镜像发生错误,重试失败,退出。", step="callback", status="failure") return self.log.info("镜像推送到本地仓库完成。") # self._tag(image, inner_image) # self._push(inner_image) has_download = True else: self.log.error("下载镜像发生错误。", step="callback", status="failure") logger.error("mq_work.image_manual", "download image failed! image:{}".format(image)) except Exception as e: self.log.error("镜像操作发生错误。{0}".format(e.__str__()), step="callback", status="failure") logger.exception("mq_work.image_manual", e) version_status = { "final_status": "failure", } if has_download: self.log.info("应用同步完成。", step="app-image", status="success") version_body = { "type": 'image', "path": local_image, "event_id": self.event_id } version_status['final_status'] = "success" try: self.region_client.update_version_region( json.dumps(version_body)) self.region_client.update_version_event( self.event_id, json.dumps(version_status)) except Exception as e: pass try: self.api.update_iamge(tenant_name, service_alias, local_image) self.log.info("应用信息更新完成,开始启动应用。", step="app-image", status="success") self.api.start_service(tenant_name, service_alias, event_id) except Exception as e: logger.exception(e) self.log.error("应用自动启动失败。请手动启动", step="callback", status="failure") else: try: self.region_client.update_version_event( self.event_id, json.dumps(version_status)) except Exception as e: pass self.log.error("应用同步失败。", step="callback", status="failure") def queryServiceStatus(self, service_id): try: res, body = self.region_api.is_service_running(service_id) logger.info( 'mq_work.image_manual', "service_id=" + service_id + ";body=" + json.dumps(body)) status = body.get(service_id, "closed") if status == "running": self.log.debug("依赖的应用状态已经为运行中。", step="worker") return True except: pass self.log.debug("依赖的应用状态不是运行中,本应用稍后启动。", step="worker") return False def get_image_property(self, image, name): query_format = '{{.%s}}' % name try: output = shell.call("{2} inspect -f '{0}' {1}".format( query_format, image, DOCKER_BIN)) if output == '<no value>': return None else: return output[0].rstrip('\n') except shell.ExecException, e: logger.exception("mq_work.image_manual", e) return None
class CodeCheck(): watching_topics = ('service_event_msg', ) required_configs = ('region', 'userconsole', 'etcd.lock') def __init__(self, job, *args, **kwargs): self.job = job self.configs = kwargs.get("config") self.user_cs_client = UserConsoleAPI(conf=self.configs['userconsole']) self.region_client = RegionBackAPI() task = json.loads(self.job.body) self.base_dir = kwargs.get('base_dir') for k in ('tenant_id', 'service_id', 'action'): setattr(self, k, task[k]) if 'event_id' in task: self.event_id = task["event_id"] self.log = EventLog().bind(event_id=self.event_id) else: self.event_id = "system" self.log = EventLog().bind(event_id=self.event_id) self.task = task self.locker = TaskLocker(conf=self.configs['etcd']) # self.waittime = int(task['wait_time']) self.log.info(u"worker已收到异步任务。", step="worker") def do_work(self): logger.info('mq_work.service_event', "plugin %s execute start" % __name__) self.log.debug(u"代码检查异步处理开始。", step="worker", status="start") self.code_check() logger.info('mq_work.service_event', "plugin %s execute finished" % __name__) def code_check(self): git_url = self.task['git_url'] check_type = self.task['check_type'] code_version = self.task['code_version'] git_project_id = self.task['git_project_id'] code_from = self.task['code_from'] url_repos = self.task['url_repos'] lock_id = 'code_check.' + self.service_id logger.info( 'mq_work.code_check', "git_url {0},check_type {1}, code_version {2},git_project_id {3},code_from {4},url_repos {5} ". format(git_url, check_type, code_version, git_project_id, code_from, url_repos)) try: if self.locker.exists(lock_id): logger.info('mq_work.code_check', "lock_id {} exists, do nothing".format(lock_id)) self.log.info( 'lock_id {} exists, do nothing'.format(lock_id), step="check_exist") return self.locker.add_lock(lock_id, bytes(git_url)) logger.info('add lock_id {}'.format(lock_id), step="check-exist") except Exception, e: pass logger.info('mq_work.code_check', 'added lock <{}> for [{}]'.format( lock_id, git_url)) logger.info( 'mq_work.code_check', self.tenant_id + "=" + self.service_id + " start code check") if self.event_id: self.log.info( "代码检测{0},{1} 开始".format(self.tenant_id, self.service_id), step="check-start") cmd = '/bin/bash {0}/scripts/detect.sh {1} {2} "{3}" {4}'.format( self.base_dir, self.tenant_id, self.service_id, git_url, self.base_dir) try: output = Executer.call(cmd) self.requestConsole(self.service_id, output[0].rstrip('\n'), check_type, git_url, code_version, git_project_id, code_from, url_repos) if self.event_id: self.log.info("代码检测完成,请重新部署", step="last", status="success") except Executer.ExecException, e: logger.info('mq_work.code_check', 'code check failed') logger.info('mq_work.code_check', e) logger.info('mq_work.code_check', e.output) self.log.error( "代码检测异常 {}".format(e), step="callback", status="failure")
class RepoBuilder(): def __init__(self, task, *args, **kwargs): self.configs = kwargs.get("config") self.region_api = RegionAPI(conf=self.configs['region']) self.api = ACPAPI(conf=self.configs['region']) self.user_cs_client = UserConsoleAPI(conf=self.configs['userconsole']) self.repo_url = task['repo_url'] self.region_client = RegionBackAPI() self.tenant_id = task['tenant_id'] self.service_id = task['service_id'] self.tenant_name = task['tenant_name'] self.service_alias = task['service_alias'] self.deploy_version = task['deploy_version'] self.action = task['action'] if 'event_id' in task: self.event_id = task["event_id"] self.log = EventLog().bind(event_id=self.event_id) else: self.event_id = "" self.log = EventLog().bind(event_id=self.event_id) self.operator = task['operator'] self.build_envs = task.get('envs', {}) self.expire = task.get('expire', 60) self.start_time = int(time.time()) # self.source_dir = '/tmp/goodrain_web' self.source_dir = SOURCE_DIR.format( tenantId=self.tenant_id, serviceId=self.service_id) self.cache_dir = CACHE_DIR.format( tenantId=self.tenant_id, serviceId=self.service_id) self.tgz_dir = TGZ_DIR.format( tenantId=self.tenant_id, serviceId=self.service_id) self.build_log_dir = BUILD_LOG_DIR.format( tenantId=self.tenant_id, serviceId=self.service_id) self.build_cmd = 'plugins/scripts/build.pl' @property def build_name(self): return self.service_id[:8] + '_' + self.deploy_version @property def is_expired(self): if hasattr(self, 'expire'): current_time = int(time.time()) return bool(current_time - self.start_time > self.expire) else: return False def prepare(self): if os.path.exists(self.source_dir): shutil.rmtree(self.source_dir) for d in (self.source_dir, self.cache_dir, self.tgz_dir, self.build_log_dir): if not os.path.exists(d): os.makedirs(d) def clone(self): self.log.info("开始拉取代码。。", step="build-worker") # code, output = shell.runsingle("git clone --branch master --depth 1 {0} {1}".format(self.repo_url, self.source_dir)) result = False num = 0 while num < 2: try: shell.call("timeout -k 9 {2} git clone {0} {1}".format( self.repo_url, self.source_dir, CLONE_TIMEOUT)) result = True break except shell.ExecException, e: num = num + 1 self.prepare() if num < 2: self.log.error( "拉取代码发生错误,开始重试 {}".format(e.message), status="failure", step="worker-clone") else: self.log.error( "拉取代码发生错误,部署停止 {}".format(e.message), status="failure", step="callback") logger.exception('build_work.main', e) result = False logger.info('build_work.main', "git clone num=" + str(num)) return result
class CodeCheck(): watching_topics = ('service_event_msg', ) required_configs = ('region', 'userconsole', 'etcd.lock') def __init__(self, job, *args, **kwargs): self.job = job self.configs = kwargs.get("config") self.user_cs_client = UserConsoleAPI(conf=self.configs['userconsole']) self.region_client = RegionBackAPI() task = json.loads(self.job.body) self.base_dir = kwargs.get('base_dir') for k in ('tenant_id', 'service_id', 'action'): setattr(self, k, task[k]) if 'event_id' in task: self.event_id = task["event_id"] self.log = EventLog().bind(event_id=self.event_id) else: self.event_id = "system" self.log = EventLog().bind(event_id=self.event_id) self.task = task self.locker = TaskLocker(conf=self.configs['etcd']) # self.waittime = int(task['wait_time']) self.log.info(u"worker已收到异步任务。", step="worker") def do_work(self): logger.info('mq_work.service_event', "plugin %s execute start" % __name__) self.log.debug(u"代码检查异步处理开始。", step="worker", status="start") self.code_check() logger.info('mq_work.service_event', "plugin %s execute finished" % __name__) def code_check(self): git_url = self.task['git_url'] check_type = self.task['check_type'] code_version = self.task['code_version'] git_project_id = self.task['git_project_id'] code_from = self.task['code_from'] url_repos = self.task['url_repos'] lock_id = 'code_check.' + self.service_id logger.info( 'mq_work.code_check', "git_url {0},check_type {1}, code_version {2},git_project_id {3},code_from {4},url_repos {5} " .format(git_url, check_type, code_version, git_project_id, code_from, url_repos)) try: if self.locker.exists(lock_id): logger.info('mq_work.code_check', "lock_id {} exists, do nothing".format(lock_id)) self.log.info('lock_id {} exists, do nothing'.format(lock_id), step="check_exist") return self.locker.add_lock(lock_id, bytes(git_url)) logger.info('add lock_id {}'.format(lock_id), step="check-exist") except Exception, e: pass logger.info('mq_work.code_check', 'added lock <{}> for [{}]'.format(lock_id, git_url)) logger.info( 'mq_work.code_check', self.tenant_id + "=" + self.service_id + " start code check") if self.event_id: self.log.info("代码检测{0},{1} 开始".format(self.tenant_id, self.service_id), step="check-start") cmd = '/bin/bash {0}/scripts/detect.sh {1} {2} "{3}" {4}'.format( self.base_dir, self.tenant_id, self.service_id, git_url, self.base_dir) try: output = Executer.call(cmd) self.requestConsole(self.service_id, output[0].rstrip('\n'), check_type, git_url, code_version, git_project_id, code_from, url_repos) if self.event_id: self.log.info("代码检测完成,请重新部署", step="last", status="success") except Executer.ExecException, e: logger.info('mq_work.code_check', 'code check failed') logger.info('mq_work.code_check', e) logger.info('mq_work.code_check', e.output) self.log.error("代码检测异常 {}".format(e), step="callback", status="failure")
class AppSlug(): def __init__(self, job, *args, **kwargs): self.job = job self.configs = kwargs.get("config") self.region_api = RegionAPI(conf=self.configs['region']) self.oss_api = OssAPI(conf=self.configs['oss']['ali_shanghai']) self.locker = TaskLocker(conf=self.configs['etcd']) self.user_cs_client = UserConsoleAPI(conf=self.configs['userconsole']) self.api = ACPAPI(conf=self.configs['region']) self.region_client = RegionBackAPI() self.slug_configs = self.configs["publish"]["slug"] self.is_region_slug = self.slug_configs.get('all_region_ftp') self.is_oss_ftp = self.slug_configs.get('oss_ftp') # 用户文件存储路径 self.SRV_SLUG_BASE_DIR = self.slug_configs.get( 'slug_path') + '{tenantId}/slug/{serviceId}/{deployVersion}.tgz' # 数据中心slug存储路径 self.SLUG_PATH = self.slug_configs.get( 'curr_region_dir') + '{serviceKey}/{appVersion}.tgz' self.CURR_REGION_PATH = self.slug_configs.get( 'curr_region_path') + self.SLUG_PATH # 区域中心slug的ftp配置 self.ALL_REGION_FTP_HOST = self.slug_configs.get('all_region_ftp_host') self.ALL_REGION_FTP_PORT = self.slug_configs.get('all_region_ftp_port') self.ALL_REGION_FTP_USERNAME = self.slug_configs.get( 'all_region_username') self.ALL_REGION_FTP_PASSWORD = self.slug_configs.get( 'all_region_password') self.ALL_REGION_FTP_NAMESPACE = self.slug_configs.get( 'all_region_namespace') self.ALL_REGION_FTP_PATH = self.ALL_REGION_FTP_NAMESPACE + '{serviceKey}/{appVersion}.tgz' # oss存储路径 CLOUD_ASSISTANT = self.configs.get('CLOUD_ASSISTANT') self.OSS_BUCKET = self.slug_configs.get('oss_bucket', "") self.OSS_OBJECT_NAME = CLOUD_ASSISTANT + '/{serviceKey}/{appVersion}.tgz' logger.debug("mq_work.app_slug", 'init app slug') def do_work(self): try: logger.debug("mq_work.app_slug", 'get task....{}'.format(self.job.body)) task = json.loads(self.job.body) self.task = task if "event_id" in self.task: self.event_id = task["event_id"] self.log = EventLog().bind( event_id=self.event_id, step="image_manual") else: self.event_id = "" self.log = EventLog().bind(event_id="", step="image_manual") if task['action'] == 'create_new_version': self.log.info("开始分享新版本应用。") self.create_new_version() elif task['action'] == 'download_and_deploy': self.log.info("开始同步应用。") self.download_and_deploy() elif task['action'] == 'delete_old_version': self.log.info("开始删除旧版本应用。") self.delete_old_version() except Exception as e: logger.exception('mq_work.app_slug', e) def _upload_ftp(self, service_key, app_version, md5file): """ 上传文件到ftp """ utils = FTPUtils( host=self.ALL_REGION_FTP_HOST, username=self.ALL_REGION_FTP_USERNAME, password=self.ALL_REGION_FTP_PASSWORD, namespace=self.ALL_REGION_FTP_NAMESPACE, port=self.ALL_REGION_FTP_PORT) # 检查service_key对应的文件是否存在,不存在生成 service_dir = self.ALL_REGION_FTP_NAMESPACE + service_key logger.debug("mq_work.app_slug", 'slug task is {}'.format(self.task)) logger.debug("mq_work.app_slug", '*******upload dir is {}'.format(service_dir)) utils.check_dir(service_dir) # 上传文件 curr_region_slug = self.CURR_REGION_PATH.format( serviceKey=service_key, appVersion=app_version) logger.debug("mq_work.app_slug", '*******upload file path is {}'.format(curr_region_slug)) utils.upload(service_dir, curr_region_slug) # 上传md5文件 if md5file: utils.upload(service_dir, md5file) return True def _create_md5(self, md5string, dest_slug_file): try: md5file = dest_slug_file + ".md5" f = open(md5file, "w") f.write(md5string) f.close() return md5file except Exception as e: logger.error("mq_work.app_slug", "sum file md5 filed!") logger.exception("mq_work.app_slug", e) return None def _check_md5(self, md5string, md5file): try: f = open(md5file) new_md5 = f.readline() return md5string == new_md5 except Exception as e: logger.error("mq_work.app_slug", "check md5 filed!") logger.exception("mq_work.app_slug", e) return False def create_new_version(self): service_key = self.task['service_key'] app_version = self.task['app_version'] service_id = self.task['service_id'] deploy_version = self.task['deploy_version'] tenant_id = self.task['tenant_id'] dest = self.task['dest'] share_id = self.task.get('share_id', None) # 检查数据中心下路径是否存在 source_slug_file = self.SRV_SLUG_BASE_DIR.format( tenantId=tenant_id, serviceId=service_id, deployVersion=deploy_version) self.log.debug("数据中心文件路径{0}".format(source_slug_file)) # 当前数据中心文件名称 dest_slug_file = self.CURR_REGION_PATH.format( serviceKey=service_key, appVersion=app_version) self.log.debug('当前数据中心文件名称'.format(dest_slug_file)) # 检查目录是否存在 curr_region_dir = os.path.dirname(dest_slug_file) if not os.path.exists(curr_region_dir): os.makedirs(curr_region_dir) # 复制文件 self.log.debug( "开始复制文件 file {0} to {1}".format(source_slug_file, dest_slug_file)) shutil.copyfile(source_slug_file, dest_slug_file) # 计算md5 md5string = get_md5(source_slug_file) # 生成md5file md5file = self._create_md5(md5string, dest_slug_file) if md5file is None: self.log.error("md5文件没有生成。") # 区域中心对象存储,使用ftp slug = self.SLUG_PATH.format( serviceKey=service_key, appVersion=app_version) if dest == "yb": data = { 'service_key': service_key, 'app_version': app_version, 'slug': slug, 'image': "", 'dest_yb': True, 'dest_ys': False, } if share_id is not None: data['share_id'] = share_id try: self.region_client.service_publish_new_region(data) except Exception as e: self.region_client.service_publish_failure_region(data) self.log.error( "云帮应用本地发布失败,保存publish 失败。{0}".format(e.message), step="callback", status="failure") pass if self.is_region_slug: try: self.log.info("开始上传应用到本地云帮") self._upload_ftp(service_key, app_version, md5file) logger.debug("mq_work.app_slug", "*******ftp upload success!") # self.update_publish_event(event_id=event_id, status='end', desc=u"云帮应用本地发布完毕") self.user_cs_client.service_publish_success( json.dumps(data)) try: self.region_client.service_publish_success_region(data) except Exception as e: self.region_client.service_publish_failure_region(data) logger.exception(e) pass self.log.info("云帮应用本地发布完毕", step="last", status="success") except Exception as e: logger.error("mq_work.app_slug", "*******ftp upload failed") logger.exception("mq_work.app_slug", e) self.region_client.service_publish_failure_region(data) self.log.info( "云帮应用本地发布失败。{}".format(e.message), step="callback", status="failure") else: self.user_cs_client.service_publish_success(json.dumps(data)) try: self.region_client.service_publish_success_region(data) except Exception as e: self.region_client.service_publish_failure_region(data) logger.exception(e) pass self.log.info("云帮应用本地发布完毕", step="last", status="success") elif dest == "ys": data = { 'service_key': service_key, 'app_version': app_version, 'slug': slug, 'image': "", 'dest_ys': True, 'dest_yb': False } if share_id is not None: data['share_id'] = share_id try: self.region_client.service_publish_new_region(data) except Exception as e: self.region_client.service_publish_failure_region(data) self.log.error( "云帮应用本地发布失败,保存publish 失败。{0}".format(e.message), step="callback", status="failure") pass if self.is_oss_ftp: try: self.log.info("开始上传应用到云市") self._upload_ftp(service_key, app_version, md5file) logger.debug("mq_work.app_slug", "*******ftp upload success!") self.log.info("云市应用发布完毕", step="last", status="success") self.user_cs_client.service_publish_success( json.dumps(data)) try: self.region_client.service_publish_success_region(data) except Exception as e: logger.exception(e) self.region_client.service_publish_failure_region(data) pass except Exception as e: logger.error("mq_work.app_slug", "*******ftp upload failed, {0}".format(e)) self.region_client.service_publish_failure_region(data) self.log.error( "云市应用发布失败.", status="failure", step="callback") else: self.user_cs_client.service_publish_success(json.dumps(data)) try: self.region_client.service_publish_success_region(data) except Exception as e: logger.exception(e) self.region_client.service_publish_failure_region(data) pass self.log.info("云市应用发布完毕", step="last", status="success") def _download_ftp(self, service_key, app_version, namespace, is_md5=False): """ 云帮ftp下载文件 """ utils = FTPUtils( host=self.ALL_REGION_FTP_HOST, username=self.ALL_REGION_FTP_USERNAME, password=self.ALL_REGION_FTP_PASSWORD, namespace=self.ALL_REGION_FTP_NAMESPACE, port=self.ALL_REGION_FTP_PORT) logger.info("mq_work.app_slug", "*******[download]download file from ftp") # 检查service_key对应的文件是否存在,不存在生成 remote_file = self.ALL_REGION_FTP_PATH.format( serviceKey=service_key, appVersion=app_version) if is_md5: remote_file += ".md5" if not namespace: logger.info("mq_work.app_slug", "*******[download]namespace is null") logger.error("mq_work.app_slug", "*******[download]namespace is null") else: logger.info("mq_work.app_slug", "*******[download]namespace is {}".format(namespace)) remote_file = "../" + namespace + "/" + remote_file logger.info("mq_work.app_slug", "*******[download]remote file is {}".format(remote_file)) curr_region_slug = self.CURR_REGION_PATH.format( serviceKey=service_key, appVersion=app_version) if is_md5: curr_region_slug += ".md5" logger.info( "mq_work.app_slug", "*******[download]curr_region_slug is {}".format(curr_region_slug)) return utils.download(remote_file, curr_region_slug) def _download_ftp_market(self, service_key, app_version, namespace, is_md5=False): """ 云市ftp下载文件 """ utils = FTPUtils( host=self.ALL_REGION_FTP_HOST, username=self.ALL_REGION_FTP_USERNAME, password=self.ALL_REGION_FTP_PASSWORD, namespace=self.ALL_REGION_FTP_NAMESPACE, port=self.ALL_REGION_FTP_PORT) logger.info("mq_work.app_slug", "*******[download]download file from ftp") # 检查service_key对应的文件是否存在,不存在生成 remote_file = self.ALL_REGION_FTP_PATH.format( serviceKey=service_key, appVersion=app_version) if is_md5: remote_file += ".md5" if not namespace: logger.info("mq_work.app_slug", "*******[download]namespace is null") logger.error("mq_work.app_slug", "*******[download]namespace is null") else: logger.info("mq_work.app_slug", "*******[download]namespace is {}".format(namespace)) remote_file = "../" + namespace + "/" + remote_file logger.info("mq_work.app_slug", "*******[download]remote file is {}".format(remote_file)) curr_region_slug = self.CURR_REGION_PATH.format( serviceKey=service_key, appVersion=app_version) if is_md5: curr_region_slug += ".md5" logger.info( "mq_work.app_slug", "*******[download]curr_region_slug is {}".format(curr_region_slug)) return utils.download(remote_file, curr_region_slug) def download_and_deploy(self): """ 下载slug包 """ def start_service(service_id, deploy_version, operator): # body = { # "deploy_version": deploy_version, # "operator": operator, # "event_id": self.event_id # } body = { "deploy_version": deploy_version, "event_id": self.event_id } try: # logger.info("mq_work.app_slug", "start service {}:{}".format(service_id, deploy_version)) self.log.info("开始调用api启动应用。") self.api.upgrade_service(self.tenant_name, self.service_alias, json.dumps(body)) # self.region_api.start_service(service_id, json.dumps(body)) except self.region_api.CallApiError, e: self.log.info( "开始调用api启动应用失败。{}".format(e.message), step="callback", status="failure") logger.exception("mq_work.app_slug", e) service_key = self.task['app_key'] namespace = self.task['namespace'] app_version = self.task['app_version'] tenant_name = self.task['tenant_name'] service_alias = self.task['service_alias'] event_id = self.task['event_id'] # 检查数据中心的是否存在slug包 dest_slug_file = self.CURR_REGION_PATH.format( serviceKey=service_key, appVersion=app_version) logger.info("mq_work.app_slug", "dest_slug_file:{}".format(dest_slug_file)) ftp_ok = False try: # 检查当前服务器是否有slug文件 if os.path.exists(dest_slug_file): self.log.debug("当前服务器存在本应用。本机同步开始") md5string = get_md5(dest_slug_file) # 检查云帮ftp是否打开, 下载md5进行校验 md5_ok = False if self.is_region_slug: self.log.debug("文件MD5校验开始。") try: md5_ok = self._download_ftp(service_key, app_version, namespace, True) self.log.info("MD5校验完成。") except Exception as e: logger.info( "mq_work.app_slug", "download md5 file from cloudassistant ftp failed!" ) self.log.error( "MD5校验失败。{}".format(e.message), step="callback", status="failure") logger.exception("mq_work.app_slug", e) # md5未下载并且云市ftp开启 if not md5_ok and self.is_oss_ftp: self.log.info("MD5校验不通过。开始从云市同步新版本。") try: md5_ok = self._download_ftp_market( service_key, app_version, namespace, True) except Exception as e: self.log.info( "从云市同步新版本发生异常。{}".format(e.message), step="callback", status="failure") logger.exception("mq_work.app_slug", e) if md5_ok: md5file = dest_slug_file + ".md5" same_file = self._check_md5(md5string, md5file) if same_file: logger.debug("mq_work.app_slug", "md5 check same.") ftp_ok = True else: logger.debug( "mq_work.app_slug", "file md5 is changed, now delete old file") os.remove(dest_slug_file) else: logger.debug("mq_work.app_slug", "md5file download failed, now delete slug") os.remove(dest_slug_file) # 检查当前服务器是否有slug文件 if not os.path.exists(dest_slug_file): curr_region_dir = os.path.dirname(dest_slug_file) if not os.path.exists(curr_region_dir): os.makedirs(curr_region_dir) logger.debug("mq_work.app_slug", "now check ftp:".format(self.is_region_slug)) # 云帮ftp开关是否打开 if self.is_region_slug: logger.debug('mq_work.app_slug', 'now check file on ftp!') try: ftp_ok = self._download_ftp(service_key, app_version, namespace) except Exception as e: logger.info("mq_work.app_slug", "download object failed") logger.exception("mq_work.app_slug", e) logger.debug( "mq_work.app_slug", "*******[ftp download slug]result:==={}".format( ftp_ok)) # 判断是否需要从云市上下载,未下载并且云市ftp开启 if not ftp_ok and self.is_oss_ftp: logger.info( "mq_work.app_slug", "now download from hub ftp:{}".format(dest_slug_file)) ftp_ok = self._download_ftp_market(service_key, app_version, namespace) logger.debug( "mq_work.app_slug", "*******[ftp download slug]result:==={}".format( ftp_ok)) else: ftp_ok = True except Exception as e: logger.exception("mq_work.app_slug", e) version_status = { "final_status":"failure", } if ftp_ok: self.log.info("应用同步完成,开始启动应用。", step="app-image", status="success") version_body = { "type": 'slug', "path": dest_slug_file, "event_id": self.event_id } version_status = { "final_status":"success", } try: self.region_client.update_version_region(json.dumps(version_body)) self.region_client.update_version_event(self.event_id,json.dumps(version_status)) except Exception as e: pass try: body = { "deploy_version": self.task['deploy_version'], "event_id": self.event_id } # self.api.start_service(tenant_name, service_alias, event_id) self.api.upgrade_service(self.task['tenant_name'], self.task['service_alias'], json.dumps(body)) except Exception as e: logger.exception(e) self.log.error( "应用自动启动失败。请手动启动", step="callback", status="failure") else: self.log.error("应用同步失败。", step="callback", status="failure") try: self.region_client.update_version_event(self.event_id,json.dumps(version_status)) except Exception as e: self.log.error("更新version信息失败", step="app-slug") pass
class AppImage(): def __init__(self, job, *args, **kwargs): self.job = job self.configs = kwargs.get("config") self.region_api = RegionAPI(conf=self.configs["region"]) self.region_client = RegionBackAPI() self.api = ACPAPI(conf=self.configs['region']) image_config = self.configs["publish"]["image"] self.region_registry = RegistryAPI( host=image_config.get('curr_registry')) self.oss_registry = RegistryAPI(host=image_config.get('all_registry')) self.region_registry.set_log_topic('mq_work.app_image') self.oss_registry.set_log_topic('mq_work.app_image') self.locker = TaskLocker(conf=self.configs['etcd']) self.namespace = image_config.get('oss_namespace') self.user_cs_client = UserConsoleAPI(conf=self.configs["userconsole"]) self.hubclient = HubUtils(image_config) # 是否配置oss.goodrain.me self.is_region_image = image_config.get('all_region_image') self.is_oss_image = image_config.get('oss_image') def do_work(self): try: task = json.loads(self.job.body) self.task = task if "event_id" in self.task: self.event_id = task["event_id"] self.log = EventLog().bind(event_id=self.event_id) else: self.event_id = "" self.log = EventLog().bind(event_id="") if task['action'] == 'create_new_version': self.log.info("开始发布升级应用。", step="app-image") self.create_new_version() elif task['action'] == 'download_and_deploy': self.log.info("开始同步和部署应用。", step="app-image") self.download_and_deploy() elif task['action'] == 'delete_old_version': self.log.info("开始删除旧版本应用。", step="app-image") self.delete_old_version() except Exception as e: if self.log: self.log.error( "从云市部署应用失败。{}".format(e.message), step="callback", status="failure") logger.exception('mq_work.app_image', e) def create_new_version(self): image = self.task['image'] service_key = self.task['service_key'] app_version = self.task['app_version'] oss_image = self.oss_registry.rename_image(image) dest = self.task['dest'] share_id = self.task.get("share_id", None) if dest == "yb": if self.region_registry.exist_image(image): logger.debug("mq_work.app_image", "now local exists, oss doesnot exists") data = { 'service_key': service_key, 'app_version': app_version, 'image': image, 'dest_yb': True, 'dest_ys': False, 'slug': "" } if share_id is not None: data["share_id"] = share_id try: self.region_client.service_publish_new_region(data) except Exception as e: self.region_client.service_publish_failure_region(data) self.log.error("云帮应用本地发布失败,保存publish 失败。{0}".format(e.message),step="callback",status="failure") pass if self.is_region_image and not self.oss_registry.exist_image( oss_image): try: self.log.info("开始拉取镜像。") ok = self._pull(image) if not ok: self.log.error( "拉取镜像发生错误,构建退出。", step="callback", status="failure") self.region_client.service_publish_failure_region(data) return image_id = self.get_image_property(image, 'Id') self.log.info("拉取镜像完成。") self._tag(image_id, oss_image) self.log.info("镜像更改tag完成。开始上传镜像到云帮") ok = self._push(oss_image) if not ok: self.log.error( "拉取镜像发生错误,构建退出。", step="callback", status="failure") self.region_client.service_publish_failure_region(data) return self.log.info("上传镜像到云帮完成") # 发送通知到web self.user_cs_client.service_publish_success( json.dumps(data)) try: self.region_client.service_publish_success_region(data) except Exception as e: logger.exception(e) self.region_client.service_publish_failure_region(data) pass self.log.info( "云帮应用发布完毕", step="last", status="success") except (shell.ExecException, Exception), e: logger.exception("mq_work.app_image", e) logger.error("mq_work.app_image", e) self.region_client.service_publish_failure_region(data) self.log.error( "云帮应用发布失败 {}".format(e.message), step="callback", status="failure") else: # 发送通知到web self.user_cs_client.service_publish_success( json.dumps(data)) try: self.region_client.service_publish_success_region(data) except Exception as e: self.region_client.service_publish_failure_region(data) logger.exception(e) pass self.log.info("云帮应用发布完毕", step="last", status="success") else: self.log.info("镜像不存在,发布失败", step="callback", status="failure") elif dest == "ys": # 当前有镜像并且云市的image数据中心开启 if self.region_registry.exist_image(image) and self.is_oss_image: req = { 'service_key': service_key, 'app_version': app_version, 'image': image, 'slug': "", 'dest_ys': True, 'dest_yb': False } if share_id is not None: req["share_id"] = share_id try: self.region_client.service_publish_new_region(data) except Exception as e: self.region_client.service_publish_failure_region(data) self.log.info("云帮应用本地发布失败,保存publish 失败。{0}".format(e.message),step="callback",status="failure") pass # self.region_client.service_publish_new_region(req) self.log.info("开始上传镜像到云市") # 修改image name hub_image = self.hubclient.rename_image(image) logger.info("mq_work.app_image", 'hub_image={}'.format(hub_image)) # 检查是否存在 data = self.hubclient.parse_image(image) logger.info("mq_work.app_image", 'data={}'.format(data)) # 判断tag是否存在, tag_exists = self.hubclient.check(data.name, data.tag) logger.info("mq_work.app_image", 'tag_exists={}'.format(tag_exists)) try: self.log.info("开始从云帮拉取镜像。") ok = self._pull(image) if not ok: self.log.error( "拉取镜像发生错误,构建退出。", step="callback", status="failure") self.region_client.service_publish_failure_region(req) return image_id = self.get_image_property(image, 'Id') self.log.info("从云帮拉取镜像完成,更改镜像TAG") self._tag(image_id, hub_image) self.log.info("更改镜像TAG完成,开始上传镜像到云市") ok = self._push(hub_image) if not ok: self.log.error( "拉取镜像发生错误,构建退出。", step="callback", status="failure") self.region_client.service_publish_failure_region(req) return self.log.info("上传镜像到云市完成。") # 发送通知到web self.user_cs_client.service_publish_success( json.dumps(req)) try: self.region_client.service_publish_success_region(req) except Exception as e: self.region_client.service_publish_failure_region(req) logger.exception(e) pass self.log.info("云市应用发布完毕", step="last", status="success") except (shell.ExecException, Exception), e: logger.exception("mq_work.app_image", e) logger.error("mq_work.app_image", e) self.region_client.service_publish_failure_region(req) self.log.error( "云市应用发布失败 {}".format(e.message), step="callback", status="failure")
class ImageManual(): def __init__(self, job, *args, **kwargs): self.job = job self.configs = kwargs.get("config") self.region_api = RegionAPI(conf=self.configs['region']) image_config = self.configs["publish"]["image"] self.region_client = RegionBackAPI() self.region_registry = RegistryAPI( host=image_config.get('curr_registry')) # self.region_registry.set_log_topic('mq_work.image_manual') self.oss_registry = RegistryAPI(host=image_config.get('all_registry')) self.oss_registry.set_log_topic('mq_work.image_manual') self.locker = TaskLocker(conf=self.configs['etcd']) self.api = ACPAPI(conf=self.configs['region']) self.namespace = image_config.get('oss_namespace') self.user_cs_client = UserConsoleAPI(conf=self.configs['userconsole']) def do_work(self): try: task = json.loads(self.job.body) self.task = task if "event_id" in self.task: self.event_id = task["event_id"] self.log = EventLog().bind( event_id=self.event_id, step="image_manual") else: self.event_id = "" self.log = EventLog().bind(event_id="", step="image_manual") logger.info("mq_work.image_manual", "new image_manual task: {}".format(task)) if task['action'] == 'create_new_version': self.log.info("开始升级应用。") self.create_new_version() elif task['action'] == 'download_and_deploy': self.log.info("开始下载镜像并部署应用。") self.download_and_deploy() elif task['action'] == 'delete_old_version': self.log.info("开始删除旧版本。") self.delete_old_version() except Exception as e: if self.log: self.log.error( "从自定义镜像部署应用失败。{}".format(e.message), step="callback", status="failure") logger.exception('mq_work.image_manual', e) def create_new_version(self): logger.debug("mq_work.image_manual", "now create new version and upload image") def delete_old_version(self): logger.debug("mq_work.image_manual", "now delete old version") def download_and_deploy(self): image = self.task['image'] # namespace = self.task['namespace'] tenant_name = self.task['tenant_name'] service_alias = self.task['service_alias'] event_id = self.task['event_id'] service_alias = self.task.get("service_alias", None) has_download = False inner_image = self.oss_registry.rename_image(image) inner_image = "{0}_{1}".format(inner_image, service_alias) local_image = self.region_registry.rename_image(image) local_image = "{0}_{1}".format(local_image, service_alias) # 直接下载docker image try: self.log.info("开始下载镜像:{0}".format(image)) pull_result = self._pull(image) if pull_result: # image_id = self.get_image_property(image, 'Id') self._tag(image, local_image) self.log.info("修改镜像名为:{0}".format(local_image)) ok = self._push(local_image) if not ok: self.log.error( "上传镜像发生错误,重试失败,退出。", step="callback", status="failure") return self.log.info("镜像推送到本地仓库完成。") # self._tag(image, inner_image) # self._push(inner_image) has_download = True else: self.log.error("下载镜像发生错误。", step="callback", status="failure") logger.error("mq_work.image_manual", "download image failed! image:{}".format(image)) except Exception as e: self.log.error( "镜像操作发生错误。{0}".format(e.__str__()), step="callback", status="failure") logger.exception("mq_work.image_manual", e) version_status = { "final_status":"failure", } if has_download: self.log.info("应用同步完成。", step="app-image", status="success") version_body = { "type": 'image', "path": local_image, "event_id": self.event_id } body = { "deploy_version": self.task['deploy_version'], "event_id": self.event_id, } version_status['final_status'] = "success" try: self.region_client.update_version_region(json.dumps(version_body)) self.region_client.update_version_event(self.event_id,json.dumps(version_status)) except Exception as e: pass try: self.api.update_iamge(tenant_name, service_alias, local_image) version = self.task['deploy_version'] self.log.info("应用信息更新完成,开始启动应用。", step="app-image", status="success") self.api.upgrade_service(self.task['tenant_name'], self.task['service_alias'], json.dumps(body)) # self.api.start_service(tenant_name, service_alias, event_id) except Exception as e: logger.exception(e) self.log.error( "应用自动启动失败。请手动启动", step="callback", status="failure") else: try: self.region_client.update_version_event(self.event_id,json.dumps(version_status)) except Exception as e: pass self.log.error("应用同步失败。", step="callback", status="failure") def queryServiceStatus(self, service_id): try: res, body = self.region_api.is_service_running(service_id) logger.info( 'mq_work.image_manual', "service_id=" + service_id + ";body=" + json.dumps(body)) status = body.get(service_id, "closed") if status == "running": self.log.debug("依赖的应用状态已经为运行中。", step="worker") return True except: pass self.log.debug("依赖的应用状态不是运行中,本应用稍后启动。", step="worker") return False def get_image_property(self, image, name): query_format = '{{.%s}}' % name try: output = shell.call("{2} inspect -f '{0}' {1}".format( query_format, image, DOCKER_BIN)) if output == '<no value>': return None else: return output[0].rstrip('\n') except shell.ExecException, e: logger.exception("mq_work.image_manual", e) return None