Django實現自動發佈(3發佈-升級和回退)

發佈實際上就是將服務的某個版本和一臺主機關聯,我用一張表(MicroServiceInstance)記錄了主機id、服務id、版本id,目前一臺主機只能部署一個版本,因此主機id和服務id要作聯合索引。前端

當咱們操做某個實例時(升級、回退),爲了防止其餘人也進行相關操做,要對實例當前的狀態就行判斷,這裏用 locked 標記。python

升級、回退操做相似,都是更新MicroServiceInstance表記錄的版本id,能夠放在一個視圖裏實現。git

升級回退頁面

點擊頁面的版本管理,則彈出對應服務的版本列表頁面,在這個頁面能夠進行發佈操做,以下圖:
版本列表github

而後點擊某個版本的 升級回退 ,則彈出頁面,用戶能夠選擇須要目標版本和相應的主機:
版本選擇web

因爲獲取主機相關的視圖函數還未實現,因此頁面顯示了數據接口異常shell

當打開升級頁面時,應該只能選擇比當前版本高的版本,即id號大於當前id;同理回退頁面則只能選擇比當前版本低的版本,即id號小於當前id。並且用戶只能選擇構建成功的版本進行操做。
版本升級選擇數據庫

版本號下拉框的數據由後端返回:後端

class VersionDeployActionApi(generic.View):
    def get(self, request, service_id, action, pk):
        if action not in ('upgrade', 'revert'):
            return JsonResponse({'msg': '僅支持 upgrade/revert', 'code': -1}, status=417)

        cur_id = int(pk)
        versions = MicroServiceVersion.objects.filter(
            microservice_id=service_id,
            status=BuildStatus.success.value
        ).order_by('-id')

        data = [{
            'id': item.id,
            'version': item.version,
            'enable': item.id > cur_id if action == 'upgrade' else cur_id > item.id,
        } for item in versions]

        return JsonResponse({
            'data': data,
            'count': versions.count(),
            'code': 0,
        })

實例

頁面上可供選擇的實例,在數據庫中體現爲當前版本id關聯的所有MicroServiceInstance對象,先在數據庫中初始化一些對應關係函數

python manage.py shell
from microservice.models import *
MicroServiceInstance.objects.create(microservice_id=1, version_id=2, updated_by_id=1, host_id=1)

而後是返回當前版本下的所有實例:post

class InstanceApi(generic.View):
    def get(self, request, service_id):
        query = request.GET
        version_id = query.get('version_id')
        locked = query.get('locked')

        try:
            service = MicroService.objects.get(pk=service_id)
        except MicroService.DoesNotExist:
            return JsonResponse({'msg': '資源不存在'}, status=404)

        insts = MicroServiceInstance.objects.filter(microservice=service)
        if version_id:
            insts = insts.filter(version_id=version_id)

        insts = insts.select_related('version').select_related('host').select_related('updated_by')

        if locked is not None:
            lck = False if locked in ('0', 'false') else True
            insts = insts.filter(locked=lck)

        data = [{
            'id': item.id,
            'name': service.name,
            'language': service.language,
            'version': item.version.version,
            'host': ':'.join((item.host.hostname, item.host.ip)),
            'host_id': item.host_id,
            'port': item.port,
            'tag': item.tag,
            'weight': item.weight,
            'description': item.description,
            'status': item.status,
            'status_str': item.get_status_display(),
            'is_maintain': item.is_maintain,
            'updated_by': item.updated_by.username,
            'updated': localtime(item.updated).strftime('%Y-%m-%d %H:%M:%S %Z'),
        } for item in insts]

        return JsonResponse({
            'data': data,
            'count': insts.count(),
            'code': 0,
        })

當lock爲true時,表明實例正在被操做,前端查詢時能夠帶上參數: lock=1,這樣就只返回可進行發佈的實例

執行升級/回退

頁面選好了目標版本和對應的主機後,發送數據到後端,後端判斷參數後執行相應的任務。

web開發有一條規則:永遠不要相信用戶的輸入

爲了保證服務實例數據的正確性、完整性,對用戶提交的發佈操做參數要進行嚴格校驗:

  • 校驗 service
  • 校驗 version
  • 校驗參數是否合法
  • 校驗傳入的主機id在數據庫中是否有記錄
  • 只更新db中存在且lock爲false的實例

相關的代碼實現:

# class VersionDeployActionApi 增長 post 方法

    def post(self, request, service_id, action, pk):
        params = request.POST
        q = {}
        q['dest_version'] = params.get('dest_version', '')
        q['host'] = params.get('host', '')

        if not q['dest_version']:
            return JsonResponse({'msg': '版本號不能爲空'}, status=417)

        if not q['host']:
            return JsonResponse({'msg': '主機不能爲空'}, status=417)
        else:
            if q['host'] != 'all' and (not re.match(r'[0-9,]', q['host'])):
                return JsonResponse({'msg': '主機參數錯誤,請傳入 all 或以逗號分隔的id值'}, status=417)

        # 先獲取服務
        try:
            service = MicroService.objects.get(pk=service_id)
            insts = MicroServiceInstance.objects.select_related('host').select_related('version').filter(version__id=pk)
            dest_ver = MicroServiceVersion.objects.filter(microservice=service).get(pk=q['dest_version'])
        except ObjectDoesNotExist:
            return JsonResponse({'msg': '資源不存在'}, status=404)

        db_idset = req_idset = set([x.host_id for x in insts])
        if q['host'] != 'all':
            req_idset = set([int(x) for x in q['host'].split(',') if x])
        # 若是傳入的主機id在 db 中不存在
        if req_idset - db_idset:
            return JsonResponse({'msg': '請發送正確的主機id'}, status=404)

        # 只更新db中存在 且未鎖定 的主機id
        idset = db_idset & req_idset

        st = InstanceStatus.upgrading.value if action == 'upgrade' else InstanceStatus.reverting.value
        for inst in insts:
            if inst.host_id in idset and not inst.locked:
                d = {
                    'updated_by': request.user,
                    'updated': now(),
                    'status': st,
                    'locked': True,
                }
                print d
                MicroServiceInstance.objects.filter(pk=inst.id).update(**d)

        # TODO 發起任務

        return JsonResponse({})

TODO 可使用celery發起一個耗時的任務操做,而視圖函數能夠當即返回。

頁面效果以下:
版本回退

升級、回退,相關的代碼在 這裏

相關文章
相關標籤/搜索