發佈實際上就是將服務的某個版本和一臺主機關聯,我用一張表(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開發有一條規則:永遠不要相信用戶的輸入。
爲了保證服務實例數據的正確性、完整性,對用戶提交的發佈操做參數要進行嚴格校驗:
相關的代碼實現:
# 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發起一個耗時的任務操做,而視圖函數能夠當即返回。
頁面效果以下:
升級、回退,相關的代碼在 這裏