def get_or_create_upgrade_record(self, tenant_id, group_id, group_key): """获取或创建升级记录""" recode_kwargs = { "tenant_id": tenant_id, "group_id": group_id, "group_key": group_key, "create_time": datetime.now(), } try: app_record = upgrade_repo.get_app_not_upgrade_record( status__lt=UpgradeStatus.UPGRADED.value, **recode_kwargs) except AppUpgradeRecord.DoesNotExist: from console.services.group_service import group_service service_group_keys = group_service.get_group_service_sources( group_id).values_list('group_key', flat=True) if group_key not in set(service_group_keys or []): raise AbortRequest(msg="the rainbond app is not in the group", msg_show="该组中没有这个云市应用", status_code=404) tenant = Tenants.objects.get(tenant_id=tenant_id) app = rainbond_app_repo.get_rainbond_app_qs_by_key( tenant.enterprise_id, group_key).first() if not app: raise AbortRequest(msg="No rainbond app found", msg_show="没有找到此云市应用", status_code=404) app_record = upgrade_repo.create_app_upgrade_record( group_name=app.group_name, **recode_kwargs) return app_record
def put(self, request, *args, **kwargs): """ 修改组件的组件类型标签 :param request: :param args: :param kwargs: :return: """ try: extend_method = request.data.get("extend_method", None) if not extend_method: raise AbortRequest(msg="select the application type", msg_show="请选择组件类型") if not is_support(extend_method): raise AbortRequest(msg="do not support service type", msg_show="组件类型非法") logger.debug("tenant: {0}, service:{1}, extend_method:{2}".format( self.tenant, self.service, extend_method)) app_manage_service.change_service_type(self.tenant, self.service, extend_method) result = general_message(200, "success", "操作成功") except CallRegionAPIException as e: result = general_message(e.code, "failure", e.message) return Response(result, status=result["code"])
def put(self, request, app_id, *args, **kwargs): k8s_services = request.data # data validation for k8s_service in k8s_services: # k8s_service_name if len(k8s_service.get("k8s_service_name")) > 63 or len( k8s_service.get("k8s_service_name")) == 0: raise AbortRequest( "k8s_service_name must be no more than 63 characters") if not re.match("[a-z]([-a-z0-9]*[a-z0-9])?", k8s_service['k8s_service_name']): raise AbortRequest( "regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?'" ) # service_id if not k8s_service.get("service_id"): raise AbortRequest("the field 'service_id' is required") if not k8s_service.get("port"): raise AbortRequest("the field 'port' is required") if not k8s_service.get("port_alias"): raise AbortRequest("the field 'port_alias' is required") group_service.update_kubernetes_services(self.tenant, self.region_name, app_id, k8s_services) result = general_message(200, "success", "更新成功", list=k8s_services) return Response(result)
def __change_port_alias(self, tenant, service, deal_port, new_port_alias, k8s_service_name): old_port_alias = deal_port.port_alias deal_port.port_alias = new_port_alias envs = env_var_service.get_env_by_container_port( tenant, service, deal_port.container_port) for env in envs: old_env_attr_name = env.attr_name new_attr_name = new_port_alias + env.attr_name.replace( old_port_alias, '') env.attr_name = new_attr_name if service.create_status == "complete": region_api.delete_service_env( service.service_region, tenant.tenant_name, service.service_alias, { "env_name": old_env_attr_name, "enterprise_id": tenant.enterprise_id }) # step 2 添加新的 add_env = { "container_port": env.container_port, "env_name": env.attr_name, "env_value": env.attr_value, "is_change": env.is_change, "name": env.name, "scope": env.scope, "enterprise_id": tenant.enterprise_id } region_api.add_service_env(service.service_region, tenant.tenant_name, service.service_alias, add_env) env.save() if k8s_service_name: if len(k8s_service_name) > 63: raise AbortRequest( "k8s_service_name must be no more than 63 characters") if not re.match("[a-z]([-a-z0-9]*[a-z0-9])?", k8s_service_name): raise AbortRequest( "regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?'" ) # make k8s_service_name unique try: port = port_repo.get_by_k8s_service_name( tenant.tenant_id, k8s_service_name) if port.k8s_service_name != k8s_service_name: raise ErrK8sServiceNameExists except TenantServicesPort.DoesNotExist: pass if service.create_status == "complete": body = deal_port.to_dict() body["port_alias"] = new_port_alias body["k8s_service_name"] = k8s_service_name self.__update_service_port(tenant, service.service_region, service.service_alias, body) deal_port.save()
def __check_user_name(self, user_name, eid=None): if not user_name: raise AbortRequest("empty username", "用户名不能为空") if self.is_user_exist(user_name, eid): raise AbortRequest("username already exists", "用户{0}已存在".format(user_name), status_code=409, error_code=409) r = re.compile('^[a-zA-Z0-9_\\-\\u4e00-\\u9fa5]+$') if not r.match(user_name): raise AbortRequest("invalid username", "用户名称只支持中英文下划线和中划线")
def put(self, request, enterprise_id, user_id, *args, **kwargs): roles = parse_item(request, "roles", required=True, error="at least one role needs to be specified") if not set(roles).issubset(EnterpriseRolesEnum.names()): raise AbortRequest("invalid roles", msg_show="角色不正确") if str(request.user.user_id) == user_id: raise AbortRequest("changing your role is not allowed", "不可修改自己的角色") user_services.update_roles(enterprise_id, user_id, roles) result = general_message(200, "success", None) return Response(result, 200)
def ensure_volume_mode(mode): if type(mode) != int: raise AbortRequest("mode be a number between 0 and 777 (octal)", msg_show="权限必须是在0和777之间的八进制数") regex = re.compile(r"^[0-7]{1,3}$") if not regex.match(str(mode)): raise AbortRequest("mode be a number between 0 and 777 (octal)", msg_show="权限必须是在0和777之间的八进制数") return mode
def post(self, request, *args, **kwargs): """ 为组件添加依赖组件 --- parameters: - name: tenantName description: 租户名 required: true type: string paramType: path - name: serviceAlias description: 组件别名 required: true type: string paramType: path - name: dep_service_id description: 依赖的组件的id required: true type: string paramType: form """ dep_service_id = request.data.get("dep_service_id", None) open_inner = request.data.get("open_inner", False) container_port = request.data.get("container_port", None) if not dep_service_id: return Response(general_message(400, "dependency service not specify", "请指明需要依赖的组件"), status=400) if self.service.is_third_party(): raise AbortRequest( msg="third-party components cannot add dependencies", msg_show="第三方组件不能添加依赖组件") if dep_service_id == self.service.service_id: raise AbortRequest(msg="components cannot rely on themselves", msg_show="组件不能依赖自己") code, msg, data = dependency_service.add_service_dependency( self.tenant, self.service, dep_service_id, open_inner, container_port, self.user.nick_name) if code == 201: result = general_message(code, "add dependency success", msg, list=data, bean={"is_inner": False}) return Response(result, status=code) if code != 200: result = general_message(code, "add dependency error", msg, list=data) return Response(result, status=code) result = general_message(code, msg, "依赖添加成功", bean=data.to_dict()) return Response(result, status=result["code"])
def __check_email(self, email): if not email: raise AbortRequest("empty email", "邮箱不能为空") if self.get_user_by_email(email): raise AbortRequest("email already exists", "邮箱{0}已存在".format(email)) r = re.compile(r'^[\w\-\.]+@[\w\-]+(\.[\w\-]+)+$') if not r.match(email): raise AbortRequest("invalid email", "邮箱地址不合法") if self.get_user_by_email(email): raise AbortRequest("username already exists", "邮箱已存在", status_code=409, error_code=409)
def initial(self, request, *args, **kwargs): self.user = request.user self.enterprise = TenantEnterprise.objects.filter( enterprise_id=self.user.enterprise_id).first() enterprise_user_perms = EnterpriseUserPerm.objects.filter( enterprise_id=self.user.enterprise_id, user_id=self.user.user_id).first() if enterprise_user_perms: self.is_enterprise_admin = True self.tenant_name = kwargs.get("tenantName", None) if not self.tenant_name: self.tenant_name = kwargs.get("team_name", None) if not self.tenant_name: self.tenant_name = request.META.get('HTTP_X_TEAM_NAME', None) if not self.tenant_name: self.tenant_name = self.request.COOKIES.get('team', None) if not self.tenant_name: self.tenant_name = self.request.COOKIES.get('team_name', None) if not self.tenant_name: self.tenant_name = self.request.GET.get('team_name', None) self.team_name = self.tenant_name if not self.response_region: self.response_region = request.GET.get("region_name", None) if not self.response_region: self.response_region = request.GET.get("region", None) if not self.response_region: self.response_region = request.META.get('HTTP_X_REGION_NAME', None) if not self.response_region: self.response_region = self.request.COOKIES.get( 'region_name', None) self.region_name = self.response_region if not self.response_region: raise AbortRequest("region_name not found !") if not self.tenant_name: raise AbortRequest("team_name not found !") try: self.tenant = Tenants.objects.get(tenant_name=self.tenant_name) self.team = self.tenant except Tenants.DoesNotExist: raise NotFound("tenant {0} not found".format(self.tenant_name)) if self.user.user_id == self.tenant.creater: self.is_team_owner = True self.enterprise = TenantEnterprise.objects.filter( enterprise_id=self.tenant.enterprise_id).first() self.is_enterprise_admin = False enterprise_user_perms = EnterpriseUserPerm.objects.filter( enterprise_id=self.tenant.enterprise_id, user_id=self.user.user_id).first() if enterprise_user_perms: self.is_enterprise_admin = True self.get_perms() self.check_perms(request, *args, **kwargs)
def post(self, request, group_id, record_id, *args, **kwargs): """提交回滚任务""" service_ids = parse_item(request, 'service_ids', required=True, error='service_ids is a required parameter') # 判断是不是最后一条升级记录 app_record = AppUpgradeRecord.objects.filter( tenant_id=self.tenant.tenant_id, group_id=int(group_id), status__in=(UpgradeStatus.UPGRADED.value, UpgradeStatus.ROLLBACK.value, UpgradeStatus.PARTIAL_UPGRADED.value, UpgradeStatus.PARTIAL_ROLLBACK.value, UpgradeStatus.UPGRADE_FAILED.value, UpgradeStatus.ROLLBACK_FAILED.value )).order_by('-create_time').first() if not app_record or app_record.ID != int(record_id): raise AbortRequest(msg="This upgrade cannot be rolled back", msg_show="本次升级无法回滚") service_records = app_record.service_upgrade_records.filter( status__in=(UpgradeStatus.UPGRADED.value, UpgradeStatus.ROLLBACK.value, UpgradeStatus.UPGRADE_FAILED.value, UpgradeStatus.ROLLBACK_FAILED.value), upgrade_type=ServiceUpgradeRecord.UpgradeType.UPGRADE.value, service_id__in=service_ids) if not service_records: raise AbortRequest(msg="This upgrade cannot be rolled back", msg_show="本次升级不支持回滚") services = service_repo.get_services_by_service_ids_and_group_key( app_record.group_key, service_records.values_list('service_id', flat=True) or []) market_services = [ upgrade_service.market_service_and_restore_backup( self.tenant, service, app_record.version) for service in services ] upgrade_service.send_rolling_request(market_services, self.tenant, self.user, app_record, service_records) upgrade_repo.change_app_record_status(app_record, UpgradeStatus.ROLLING.value) return MessageResponse( msg="success", bean=upgrade_service.serialized_upgrade_record(app_record))
def get_service_changes(service, tenant, version): """获取组件更新信息""" from console.services.app_actions.properties_changes import PropertiesChanges try: pc = PropertiesChanges(service, tenant) app = get_upgrade_app_version_template_app(tenant, version, pc) return pc.get_property_changes(app, level="app") except (RecordNotFound, ErrServiceSourceNotFound) as e: AbortRequest(msg=str(e)) except RbdAppNotFound as e: AbortRequest(msg=str(e))
def get_service_changes(service, tenant, version): """获取服务更新信息""" from console.services.app_actions.properties_changes import PropertiesChanges try: pc = PropertiesChanges(service) return pc.get_property_changes(tenant.enterprise_id, version) except RecordNotFound as e: AbortRequest(msg=str(e)) except RbdAppNotFound as e: AbortRequest(msg=str(e))
def get_service_changes(service, tenant, version, services): """获取组件更新信息""" from console.services.app_actions.properties_changes import \ PropertiesChanges try: pc = PropertiesChanges(service, tenant, all_component_one_model=services) upgrade_template = get_upgrade_app_template(tenant, version, pc) model_component, changes = pc.get_property_changes(template=upgrade_template, level="app") return pc.current_version, model_component, changes except (RecordNotFound, ErrServiceSourceNotFound) as e: AbortRequest(msg=str(e)) except RbdAppNotFound as e: AbortRequest(msg=str(e))
def create_app(self, tenant, region_name, app_name, note="", username="", app_store_name="", app_store_url="", app_template_name="", version="", eid=""): self.check_app_name(tenant, region_name, app_name) # check parameter for helm app app_type = AppType.rainbond.name if app_store_name or app_template_name or version: app_type = AppType.helm.name if not app_store_name: raise AbortRequest("the field 'app_store_name' is required") if not app_store_url: raise AbortRequest("the field 'app_store_url' is required") if not app_template_name: raise AbortRequest("the field 'app_template_name' is required") if not version: raise AbortRequest("the field 'version' is required") app = ServiceGroup( tenant_id=tenant.tenant_id, region_name=region_name, group_name=app_name, note=note, is_default=False, username=username, update_time=datetime.now(), create_time=datetime.now(), app_type=app_type, app_store_name=app_store_name, app_store_url=app_store_url, app_template_name=app_template_name, version=version, ) group_repo.create(app) self.create_region_app(tenant, region_name, app, eid=eid) res = app.to_dict() # compatible with the old version res["group_id"] = app.ID res['app_id'] = app.ID res['app_name'] = app.group_name return res
def save_multi_services(self, region_name, tenant, group_id, service, user, service_infos): service_source = service_source_repo.get_service_source( tenant.tenant_id, service.service_id) service_ids = [] for service_info in service_infos: code, msg_show, new_service = app_service \ .create_source_code_app(region_name, tenant, user, service.code_from, service_info["cname"], service.clone_url, service.git_project_id, service.code_version, service.server_type, oauth_service_id=service.oauth_service_id, git_full_name=service.git_full_name) if code != 200: raise AbortRequest( "Multiple services; Service alias: {}; error creating service" .format(service.service_alias), "创建多组件应用失败") # add username and password if service_source: git_password = service_source.password git_user_name = service_source.user_name if git_password or git_user_name: app_service.create_service_source_info( tenant, new_service, git_user_name, git_password) # add group code, msg_show = group_service.add_service_to_group( tenant, region_name, group_id, new_service.service_id) if code != 200: logger.debug( "Group ID: {0}; Service ID: {1}; error adding service to group" .format(group_id, new_service.service_id)) raise AbortRequest("app not found", "创建多组件应用失败", 404, 404) # save service info, such as envs, ports, etc code, msg = app_check_service.save_service_info( tenant, new_service, service_info) if code != 200: logger.debug( "Group ID: {0}; Service ID: {1}; error saving services". format(group_id, new_service.service_id)) raise AbortRequest(msg, "创建多组件应用失败") new_service = app_service.create_region_service( tenant, new_service, user.nick_name) new_service.create_status = "complete" new_service.save() service_ids.append(new_service.service_id) return service_ids
def get(self, request, *args, **kwargs): pod_name = request.GET.get("pod_name") if not pod_name: raise AbortRequest("the field 'pod_name' is required") container_name = request.GET.get("container_name") if not container_name: raise AbortRequest("the field 'container_name' is required") follow = True if request.GET.get("follow") == "true" else False stream = component_log_service.get_component_log_stream( self.tenant_name, self.region_name, self.service.service_alias, pod_name, container_name, follow) response = StreamingHttpResponse(stream, content_type="text/plain") # disabled the GZipMiddleware on this call by inserting a fake header into the StreamingHttpResponse response['Content-Encoding'] = 'identity' return response
def get(self, request, *args, **kwargs): k8s_service_name = parse_item(request, "k8s_service_name", required=True) if len(k8s_service_name) > 63: raise AbortRequest( "k8s_service_name must be no more than 63 characters") if not re.match("[a-z]([-a-z0-9]*[a-z0-9])?", k8s_service_name): raise AbortRequest( "regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?'") res = port_service.check_k8s_service_name(self.tenant.tenant_id, k8s_service_name) result = general_message(200, "success", "检测成功", bean=res) return Response(result)
def post(self, request, event_id, *args, **kwargs): """ 导入应用包 --- parameters: - name: tenantName description: 团队名称 required: true type: string paramType: path - name: event_id description: 事件ID required: true type: string paramType: path - name: scope description: 导入范围 required: true type: string paramType: form - name: file_name description: 导入文件名,多个文件名以英文逗号分隔 required: true type: string paramType: form """ try: scope = request.data.get("scope", None) file_name = request.data.get("file_name", None) team_name = request.data.get("tenant_name", None) if not scope: raise AbortRequest(msg="select the scope", msg_show="请选择导入应用可见范围") if scope == "team" and not team_name: raise AbortRequest(msg="select the team", msg_show="请选择要导入的团队") if not file_name: raise AbortRequest(msg="file name is null", msg_show="请选择要导入的文件") if not event_id: raise AbortRequest(msg="event is not found", msg_show="参数错误,未提供事件ID") files = file_name.split(",") import_service.start_import_apps(scope, event_id, files, team_name) result = general_message(200, 'success', "操作成功,正在导入") except Exception as e: logger.exception(e) result = error_message(e.message) return Response(result, status=result["code"])
def put(self, request, *args, **kwargs): data = request.data if type(data) != list: raise AbortRequest(msg="The request parameter must be a list", msg_show="请求参数必须为列表") custom_configs_service.bulk_create_or_update(data) result = general_message(200, "success", msg_show="操作成功", list=data) return Response(result, status=result["code"])
def get_or_create_upgrade_record(self, tenant_id, group_id, group_key, upgrade_group_id, is_from_cloud, market_name): """获取或创建升级记录""" recode_kwargs = { "tenant_id": tenant_id, "group_id": group_id, "group_key": group_key, "create_time": datetime.now(), "is_from_cloud": is_from_cloud, "market_name": market_name, "upgrade_group_id": upgrade_group_id, } try: return upgrade_repo.get_app_not_upgrade_record(status__lt=UpgradeStatus.UPGRADED.value, **recode_kwargs) except AppUpgradeRecord.DoesNotExist: from console.services.group_service import group_service tenant = Tenants.objects.get(tenant_id=tenant_id) service_group_keys = group_service.get_group_service_sources(group_id).values_list('group_key', flat=True) if group_key in set(service_group_keys or []): if not is_from_cloud: app = rainbond_app_repo.get_rainbond_app_qs_by_key(tenant.enterprise_id, group_key) if not app: raise ServiceHandleException( msg="the rainbond app is not in the group", msg_show="该应用中没有这个云市组件", status_code=404) app_name = app.app_name else: market = app_market_service.get_app_market_by_name(tenant.enterprise_id, market_name, raise_exception=True) app = app_market_service.get_market_app_model(market, group_key) app_name = app.app_name app_record = upgrade_repo.create_app_upgrade_record(group_name=app_name, **recode_kwargs) return app_record else: raise AbortRequest(msg="the app model is not in the group", msg_show="该应用中没有这个应用模型", status_code=404)
def _app_template_source(app_id, app_model_key, upgrade_group_id): components = group_service.get_rainbond_services(app_id, app_model_key, upgrade_group_id) if not components: raise AbortRequest("components not found", "找不到组件", status_code=404, error_code=404) component = components[0] component_source = service_source_repo.get_service_source(component.tenant_id, component.service_id) return component_source
def put(self, request, app_id, *args, **kwargs): k8s_services = request.data # data validation for k8s_service in k8s_services: if not k8s_service.get("service_id"): raise AbortRequest("the field 'service_id' is required") if not k8s_service.get("port"): raise AbortRequest("the field 'port' is required") if not k8s_service.get("port_alias"): raise AbortRequest("the field 'port_alias' is required") group_service.update_kubernetes_services(self.tenant, self.region_name, self.app, k8s_services) result = general_message(200, "success", "更新成功", list=k8s_services) return Response(result)
def parse_argument(request, key, default=None, value_type=str, required=False, error=''): """解析请求参数 :param request: django request 对象 :param key: 想要获取的请求参数名 :param default: 如果 get 不到数据返回的默认值 :param value_type: 获取到请求参数后转换的类型,支持 int, str, list 如果为 list 返回参数名为key的请求参数列表,列表内对象为 str :param required: 如果为 True,获取必填对象,获取不到返回400 :param error: 如果是必填参数,get不到时返回的错误信息 :return: key's value """ if value_type not in (bool, int, str, list): raise TypeError("value_type not in (bool, int, str, list)") if default and not isinstance(default, value_type): raise TypeError("{} isn't {}".format(default, value_type)) if value_type is bool: value_type = bool_argument get = request.GET value = get.getlist(key, default=default) if value_type is list else get.get( key, default=default) if required and (value is None or value == []): raise AbortRequest(error) return (value or None) if value_type is list else ( None if value is None else value_type(value))
def delete(self, request, *args, **kwargs): """ 删除组件的某个端口 --- parameters: - name: tenantName description: 租户名 required: true type: string paramType: path - name: serviceAlias description: 组件别名 required: true type: string paramType: path - name: port description: 端口号 required: true type: string paramType: path """ container_port = kwargs.get("port", None) if not container_port: raise AbortRequest("container_port not specify", "端口变量名未指定") data = port_service.delete_port_by_container_port( self.tenant, self.service, int(container_port), self.user.nick_name) result = general_message(200, "success", "删除成功", bean=model_to_dict(data)) return Response(result, status=result["code"])
def update_kubernetes_services(self, tenant, region_name, app_id, k8s_services): from console.services.app_config import port_service service_ids = group_service_relation_repo.list_serivce_ids_by_app_id( tenant.tenant_id, region_name, app_id) for k8s_service in k8s_services: port_service.check_k8s_service_name( tenant.tenant_id, k8s_service.get("k8s_service_name"), k8s_service["service_id"], k8s_service["port"]) # check if the given k8s_services belong to the app based on app_id if k8s_service["service_id"] not in service_ids: raise AbortRequest("service({}) not belong to app({})".format( k8s_service["service_id"], app_id)) # bulk_update is only available after django 2.2 for k8s_service in k8s_services: service = service_repo.get_service_by_service_id( k8s_service["service_id"]) port = port_repo.get_service_port_by_port(tenant.tenant_id, service.service_id, k8s_service["port"]) port_service.change_port_alias( tenant, service, port, k8s_service["port_alias"], k8s_service["k8s_service_name"], )
def parse_item(request, key, default=None, required=False, error=''): """ 解析某一个date参数 :type request: rest_framework.request.Request """ value = request.data.get(key, default) if required and value is None: raise AbortRequest(error) return value
def _app_template(enterprise_id, app_model_key, version, app_template_source): if not app_template_source.is_install_from_cloud(): _, app_version = rainbond_app_repo.get_rainbond_app_and_version(enterprise_id, app_model_key, version) else: market = app_market_repo.get_app_market_by_name( enterprise_id, app_template_source.get_market_name(), raise_exception=True) _, app_version = app_market_service.cloud_app_model_to_db_model(market, app_model_key, version) if not app_version: raise AbortRequest("app template not found", "找不到应用模板", status_code=404, error_code=404) try: app_template = json.loads(app_version.app_template) app_template["update_time"] = app_version.update_time return app_template except JSONDecodeError: raise AbortRequest("invalid app template", "该版本应用模板已损坏, 无法升级")
def get(self, request, enterprise_id, *args, **kwargs): market_name = request.GET.get("market_name", None) market_url = request.GET.get("market_url", None) access_key = request.GET.get("access_key", None) if market_url is None and market_name is None: raise AbortRequest("the field 'market_name' or 'market_url' is required") markets = app_market_service.list_bindable_markets(enterprise_id, market_name, market_url, access_key) return Response(general_message(200, "success", "获取成功", list=markets))
def put(self, request, app_id, *args, **kwargs): governance_mode = parse_item(request, "governance_mode", required=True) if governance_mode not in GovernanceModeEnum.names(): raise AbortRequest("governance_mode not in ({})".format(GovernanceModeEnum.names())) group_service.update_governance_mode(self.tenant, self.region_name, app_id, governance_mode) result = general_message(200, "success", "更新成功", bean={"governance_mode": governance_mode}) return Response(result)