예제 #1
0
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
예제 #2
0
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
예제 #3
0
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")
예제 #4
0
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
예제 #5
0
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")
예제 #6
0
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
예제 #7
0
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")
예제 #8
0
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