def list(self, request, project_id, cluster_id): """按 instance_id 聚合的 targets 列表""" params = self.params_validate(FetchTargetsSLZ) result = get_targets(project_id, cluster_id).get('data') or [] targets = self._filter_targets(result, params['show_discovered']) targets_dict = {} for instance_id, targets in itertools.groupby( sorted(targets, key=lambda x: x['instance_id']), key=lambda y: y['instance_id']): targets = list(targets) jobs = {t['job'] for t in targets} graph_url = self._gen_graph_url(project_id, cluster_id, jobs) if jobs else None targets_dict[instance_id] = { 'targets': targets, 'graph_url': graph_url, 'total_count': len(targets), 'health_count': len([t for t in targets if t['health'] == 'up']), } # 如果是共享集群,需要过滤出属于项目的命名空间的 Target # 过滤规则:targets_dict key: {namespace}/{name} 取 ns 进行检查 if get_cluster_type(cluster_id) == ClusterType.SHARED: project_namespaces = get_shared_cluster_proj_namespaces( request.ctx_cluster, request.project.english_name) targets_dict = { inst_id: target_info for inst_id, target_info in targets_dict.items() if inst_id.split('/')[0] in project_namespaces } return Response(targets_dict)
def create(self, request, project_id, is_validate_perm=True): """新建命名空间 k8s 流程:新建namespace配置文件并下发 -> 新建包含仓库账号信息的sercret配置文件并下发 -> 在paas-cc上注册 """ serializer = slz.CreateNamespaceSLZ(data=request.data, context={ 'request': request, 'project_id': project_id }) serializer.is_valid(raise_exception=True) data = serializer.data # 判断权限 cluster_id = data['cluster_id'] perm_ctx = NamespacePermCtx(username=request.user.username, project_id=project_id, cluster_id=cluster_id) self.iam_perm.can_create(perm_ctx) if get_cluster_type(cluster_id) == ClusterType.SHARED: data[ "name"] = f"{settings.SHARED_CLUSTER_NS_PREFIX}{request.project.project_code}-{data['name']}" request.audit_ctx.update_fields( resource=data['name'], description=_('集群: {}, 创建命名空间: 命名空间[{}]').format( cluster_id, data["name"])) result = self.create_flow(request, project_id, data) return response.Response(result)
def delete_hpa(request, project_id, cluster_id, ns_name, namespace_id, name): # 共享集群 HPA 不允许删除 if get_cluster_type(cluster_id) == ClusterType.SHARED: raise DeleteResourceError(_("共享集群 HPA 不支持删除")) ctx_cluster = CtxCluster.create(token=request.user.token.access_token, project_id=project_id, id=cluster_id) client = hpa_client.HPA(ctx_cluster) try: client.delete_ignore_nonexistent(name=name, namespace=ns_name) except Exception as error: logger.error("delete hpa error, namespace: %s, name: %s, error: %s", ns_name, name, error) raise DeleteResourceError(_("删除HPA资源失败")) # 删除成功则更新状态 InstanceConfig.objects.filter( namespace=namespace_id, category=K8sResourceName.K8sHPA.value, name=name).update( updator=request.user.username, oper_type=application_constants.DELETE_INSTANCE, deleted_time=timezone.now(), is_deleted=True, is_bcs_success=True, )
def list(self, request, project_id, cluster_id): """获取 ServiceMonitor 列表""" cluster_map = self._get_cluster_map(project_id) namespace_map = self._get_namespace_map(project_id) if cluster_id not in cluster_map: raise error_codes.APIError(_('集群 ID {} 不合法').format(cluster_id)) client = K8SClient(request.user.token.access_token, project_id, cluster_id, env=None) manifest = client.list_service_monitor() service_monitors = self._handle_items(cluster_id, cluster_map, namespace_map, manifest) # 共享集群需要再过滤下属于当前项目的命名空间 if get_cluster_type(cluster_id) == ClusterType.SHARED: project_namespaces = get_shared_cluster_proj_namespaces( request.ctx_cluster, request.project.english_name) service_monitors = [ sm for sm in service_monitors if sm['namespace'] in project_namespaces ] for m in service_monitors: m['is_system'] = m['namespace'] in constants.SM_NO_PERM_NAMESPACE m['iam_ns_id'] = calc_iam_ns_id(m['cluster_id'], m['namespace']) return PermsResponse( service_monitors, resource_request=NamespaceRequest(project_id=project_id, cluster_id=cluster_id), )
def sync_namespace(self, request, project_id): """同步命名空间 用来同步线上和本地存储的数据,并进行secret等的处理,保证数据一致 """ resp = paas_cc.get_all_clusters(request.user.token.access_token, project_id, desire_all_data=1) if resp.get('code') != ErrorCode.NoError: raise error_codes.APIError( f'get cluster error {resp.get("message")}') data = resp.get('data') or {} results = data.get('results') if not results: raise error_codes.ResNotFoundError( f'not found cluster in project: {project_id}') # 共享集群的命名空间只能通过产品创建,不允许同步 cluster_id_list = [ info['cluster_id'] for info in results if get_cluster_type(info['cluster_id']) != ClusterType.SHARED ] # 触发后台任务进行同步数据 sync_ns_task.delay( request.user.token.access_token, project_id, request.project.project_code, request.project.kind, cluster_id_list, request.user.username, ) request.audit_ctx.update_fields(description=_("同步项目下所有集群的命名空间")) return response.Response({'code': 0, 'message': 'task is running'})
def list(self, request, project_id, cluster_id): """获取指定资源某resource_version后变更记录""" params = self.params_validate(FetchResourceWatchResultSLZ, context={'ctx_cluster': request.ctx_cluster}) res_kind, res_version, namespace = params['kind'], params['resource_version'], params.get('namespace') watch_kwargs = { 'namespace': namespace, 'resource_version': res_version, 'timeout': DEFAULT_SUBSCRIBE_TIMEOUT, } if is_native_kind(res_kind): # 根据 Kind 获取对应的 K8S Resource Client 并初始化 resource_client = get_native_kind_resource_client(res_kind)(request.ctx_cluster) # 对于命名空间,watch_kwargs 需要补充 cluster_type,project_code 以支持共享集群的需求 if res_kind == K8sResourceKind.Namespace.value: watch_kwargs.update( {'cluster_type': get_cluster_type(cluster_id), 'project_code': request.project.english_name} ) else: # 自定义资源类型走特殊的获取 ResourceClient 逻辑 且 需要指定 Formatter resource_client = CustomObject(request.ctx_cluster, kind=res_kind, api_version=params['api_version']) watch_kwargs['formatter'] = CustomObjectCommonFormatter() try: events = resource_client.watch(**watch_kwargs) except ApiException as e: if e.status == K8S_API_GONE_STATUS_CODE: raise ResourceVersionExpired(_('ResourceVersion {} 已过期,请重新获取').format(res_version)) raise # events 默认按时间排序,取最后一个 ResourceVersion 即为最新值 latest_rv = getitems(events[-1], 'manifest.metadata.resourceVersion') if events else None response_data = {'events': events, 'latest_rv': latest_rv} return Response(response_data)
def validate_cluster_id(self, cluster_id): access_token = self.context['request'].user.token.access_token project_id = self.context['project_id'] data = app_utils.get_project_cluster_info(access_token, project_id) # 校验共享集群 if get_cluster_type(cluster_id) == ClusterType.SHARED: return cluster_id for cluster in data['results']: if cluster_id == cluster['cluster_id']: return cluster_id raise ValidationError('not found cluster, please check cluster info')
def has_permission(self, request, view): cluster_type = get_cluster_type(view.kwargs['cluster_id']) if cluster_type == ClusterType.SINGLE: return True # create 使用 request.data 中的 namespace if view.action == 'create': request_ns = request.data.get('namespace') # retrieve, update, destroy 方法使用路径参数中的 namespace else: request_ns = view.kwargs.get('namespace') return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns, request.project.english_name)
def has_permission(self, request, view): cluster_type = get_cluster_type(view.kwargs['cluster_id']) if cluster_type == ClusterType.SINGLE: return True # list, retrieve, update, destroy 方法使用路径参数中的 namespace,create 方法需要解析 request.data if view.action == 'create': request_ns = getitems(request.data, 'manifest.metadata.namespace') else: request_ns = view.kwargs.get( 'namespace') or request.query_params.get('namespace') return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns, request.project.english_name)
def get_services_by_cluster_id(self, request, params, project_id, cluster_id): """查询services""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return ErrorCode.NoError, [] access_token = request.user.token.access_token client = k8s.K8SClient(access_token, project_id, cluster_id, env=None) resp = client.get_service(params) if resp.get("code") != ErrorCode.NoError: logger.error(u"bcs_api error: %s" % resp.get("message", "")) return resp.get("code", DEFAULT_ERROR_CODE), resp.get( "message", _("请求出现异常!")) return ErrorCode.NoError, resp.get("data", [])
def get_ingress_by_cluser_id(self, request, params, project_id, cluster_id): """查询configmaps""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return 0, [] access_token = request.user.token.access_token client = k8s.K8SClient(access_token, project_id, cluster_id, env=None) resp = client.get_ingress(params) if resp.get("code") != ErrorCode.NoError: logger.error(u"bcs_api error: %s" % resp.get("message", "")) return resp.get("code", ErrorCode.UnknownError), resp.get( "message", _("请求出现异常!")) data = resp.get("data") or [] return 0, data
def get_secrets_by_cluster_id(self, request, params, project_id, cluster_id): """查询secrets""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return 0, [] search_fields = copy.deepcopy(DEFAULT_SEARCH_FIELDS) search_fields.append("data.data") params.update({"field": ",".join(search_fields)}) client = k8s.K8SClient(request.user.token.access_token, project_id, cluster_id, env=None) resp = client.get_secret(params) if resp.get("code") != ErrorCode.NoError: logger.error(u"bcs_api error: %s" % resp.get("message", "")) return resp.get("code", ErrorCode.UnknownError), resp.get("message", _("请求出现异常!")) data = resp.get("data") or [] return 0, data
def get(self, request, project_id, ns_id): cluster_type, app_status, app_id, filter_ns_id, request_cluster_id = self.get_filter_params( request, project_id) if filter_ns_id and str(ns_id) != str(filter_ns_id): return APIResponse({"data": {}}) # 获取项目下集群类型 cluster_env_map = self.get_cluster_id_env(request, project_id) # 检查命名空间属于项目 cluster_id, ns_name = self.check_ns_with_project( request, project_id, ns_id, cluster_type, cluster_env_map) # 共享集群不允许通过该接口查询应用 if get_cluster_type(cluster_id) == ClusterType.SHARED: return APIResponse({"data": {}}) inst_name = None if app_id: inst_name = self.get_inst_name(app_id) ns_name_id = self.get_namespace_name_id(request, project_id) # 根据类型进行过滤数据 category = request.GET.get("category") if not category or category not in CATEGORY_MAP.keys(): raise error_codes.CheckFailed(_("类型不正确")) client = k8s_views.GetInstances() ret_data = client.get( request, project_id, ns_id, category, inst_name, app_status, cluster_env_map, cluster_id, ns_name, ns_name_id, ) iam_ns_id = calc_iam_ns_id(cluster_id, ns_name) for inst in ret_data["instance_list"]: inst['iam_ns_id'] = iam_ns_id return PermsResponse( ret_data, NamespaceRequest(project_id=project_id, cluster_id=cluster_id), resource_data={'iam_ns_id': iam_ns_id}, )
def list(self, request, project_id, cluster_id): """获取可选 Service 列表""" client = K8SClient(request.user.token.access_token, project_id, cluster_id, env=None) resp = client.get_service({'env': 'k8s'}) services = self._slim_down_service(resp.get('data') or []) # 共享集群需要再过滤下属于当前项目的命名空间 if get_cluster_type(cluster_id) == ClusterType.SHARED: project_namespaces = get_shared_cluster_proj_namespaces( request.ctx_cluster, request.project.english_name) services = [ svc for svc in services if svc['namespace'] in project_namespaces ] return Response(services)
def get_clusters_without_ns(self, clusters, cluster_ids_with_ns): """获取不带有ns的集群""" clusters_without_ns = [] for cluster_id in clusters: if cluster_id in cluster_ids_with_ns: continue cluster = clusters[cluster_id] item = { "environment_name": get_cluster_env_name(cluster["environment"]), "environment": cluster["environment"], "cluster_id": cluster_id, "name": cluster["name"], "results": [], } if get_cluster_type(cluster_id) == ClusterType.SHARED: item["is_shared"] = True clusters_without_ns.append(item) return clusters_without_ns
def delete_single_service(self, request, project_id, project_kind, cluster_id, namespace, namespace_id, name): if get_cluster_type(cluster_id) == ClusterType.SHARED: return {"code": 400, "message": _("无法操作共享集群资源")} username = request.user.username access_token = request.user.token.access_token if namespace in K8S_SYS_NAMESPACE: return { "code": 400, "message": _("不允许操作系统命名空间[{}]").format(','.join(K8S_SYS_NAMESPACE)), } client = k8s.K8SClient(access_token, project_id, cluster_id, env=None) resp = client.delete_service(namespace, name) s_cate = 'K8sService' delete_svc_extended_routes(request, project_id, cluster_id, namespace, name) if resp.get("code") == ErrorCode.NoError: # 删除成功则更新状态 now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') InstanceConfig.objects.filter( namespace=namespace_id, category=s_cate, name=name, ).update( creator=username, updator=username, oper_type=DELETE_INSTANCE, updated=now_time, deleted_time=now_time, is_deleted=True, is_bcs_success=True, ) return { "code": resp.get("code"), "message": resp.get("message"), }
def has_permission(self, request, view): project_id, cluster_id = view.kwargs['project_id'], view.kwargs[ 'cluster_id'] cluster_type = get_cluster_type(cluster_id) if cluster_type == ClusterType.SINGLE: return True # 只有指定的数类资源可以执行订阅功能 res_kind = request.query_params.get('kind') if res_kind not in SHARED_CLUSTER_SUBSCRIBEABLE_RESOURCE_KINDS: return False # 命名空间可以直接查询,但是不属于项目的需要被过滤掉 if res_kind == K8sResourceKind.Namespace.value: return True # 可以执行订阅功能的资源,也需要检查命名空间是否属于指定的项目 request_ns = request.query_params.get('namespace') return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns, request.project.english_name)
def get_cluster_hpa_list(request, project_id, cluster_id, namespace=None): """获取基础hpa列表""" # 共享集群 HPA 不展示 if get_cluster_type(cluster_id) == ClusterType.SHARED: return [] project_code = request.project.english_name hpa_list = [] try: ctx_cluster = CtxCluster.create(token=request.user.token.access_token, project_id=project_id, id=cluster_id) client = hpa_client.HPA(ctx_cluster) formatter = HPAFormatter(cluster_id, project_code) hpa_list = client.list(formatter=formatter, namespace=namespace) except Exception as error: logger.error("get hpa list error, %s", error) return hpa_list
def has_permission(self, request, view): # 普通独立集群无需检查 if get_cluster_type(view.kwargs['cluster_id']) == ClusterType.SINGLE: return True # 共享集群等暂时只允许查询部分自定义资源 if view.kwargs['crd_name'] not in SHARED_CLUSTER_ENABLED_CRDS: return False # 检查命名空间是否属于项目且在共享集群中 # list, retrieve, destroy 方法使用路径参数中的 namespace,create, update 方法需要解析 request.data if view.action == 'create': request_ns = getitems(request.data, 'manifest.metadata.namespace') elif view.action == 'update': request_ns = request.data.get('namespace') else: request_ns = request.query_params.get('namespace') return is_proj_ns_in_shared_cluster(request.ctx_cluster, request_ns, request.project.english_name)
def get(self, request, project_id, cluster_id, namespace, name): """获取项目下所有的endpoints""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return APIResponse({"code": 400, "message": _("无法查看共享集群资源")}) params = {"name": name, "namespace": namespace} client = k8s.K8SClient(request.user.token.access_token, project_id, cluster_id, env=None) resp = client.get_endpoints(params) if resp.get("code") != ErrorCode.NoError: return APIResponse({ "code": resp.get("code", ErrorCode.UnknownError), "message": resp.get("message", _("请求出现异常!")) }) return APIResponse({ "code": ErrorCode.NoError, "data": resp.get("data"), "message": "ok" })
def batch_delete(self, request, project_id, cluster_id): """批量删除 ServiceMonitor""" params = self.params_validate(ServiceMonitorBatchDeleteSLZ) svc_monitors = params['service_monitors'] # 共享集群,过滤掉不属于项目的命名空间的 if get_cluster_type(cluster_id) == ClusterType.SHARED: project_namespaces = get_shared_cluster_proj_namespaces( request.ctx_cluster, request.project.english_name) svc_monitors = [ sm for sm in svc_monitors if sm['namespace'] in project_namespaces ] self._validate_namespace_use_perm( project_id, cluster_id, [sm['namespace'] for sm in svc_monitors]) client = K8SClient(request.user.token.access_token, project_id, cluster_id, env=None) for m in svc_monitors: self._single_service_monitor_operate_handler( client.delete_service_monitor, _('删除'), project_id, ActivityType.Delete, m['namespace'], m['name']) metrics_names = ','.join( [f"{sm['namespace']}/{sm['name']}" for sm in svc_monitors]) message = _('删除 Metrics: {} 成功').format(metrics_names) self._activity_log( project_id, request.user.username, metrics_names, message, ActivityType.Delete, ActivityStatus.Succeed, ) return Response({'successes': svc_monitors})
def list(self, request, project_id): """命名空间列表 权限控制: 必须有对应集群的使用权限 """ access_token = request.user.token.access_token valid_group_by = ['env_type', 'cluster_id', 'cluster_name'] group_by = request.GET.get('group_by') cluster_id = request.GET.get('cluster_id') # 获取全部namespace,前台分页 result = paas_cc.get_namespace_list(access_token, project_id, limit=LIMIT_FOR_ALL_DATA) if result.get('code') != 0: raise error_codes.APIError.f(result.get('message', '')) results = result["data"]["results"] or [] # 针对k8s集群过滤掉平台命名空间 results = self._ignore_ns_for_k8s(results) # 补充cluster_name字段 cluster_list = get_clusters(access_token, project_id) # 添加共享集群 cluster_list = append_shared_clusters(cluster_list) # TODO: 后续发现cluster_id不存在时,再处理 cluster_dict = {i["cluster_id"]: i for i in (cluster_list or [])} # no_vars=1 不显示变量 no_vars = request.GET.get('no_vars') if no_vars == '1': project_var = [] else: project_var = NameSpaceVariable.get_project_ns_vars(project_id) for i in results: # ns_vars = NameSpaceVariable.get_ns_vars(i['id'], project_id) ns_id = i['id'] ns_vars = [] for _var in project_var: _ns_values = _var['ns_values'] _ns_value_ids = _ns_values.keys() ns_vars.append({ 'id': _var['id'], 'key': _var['key'], 'name': _var['name'], 'value': _ns_values.get(ns_id) if ns_id in _ns_value_ids else _var['default_value'], }) i['ns_vars'] = ns_vars if i['cluster_id'] in cluster_dict: i['cluster_name'] = cluster_dict[i['cluster_id']]['name'] i['environment'] = cluster_dict[i['cluster_id']]['environment'] else: i['cluster_name'] = i['cluster_id'] i['environment'] = None if cluster_id: results = filter(lambda x: x['cluster_id'] == cluster_id, results) if group_by and group_by in valid_group_by: # 分组, 排序 results = [{ 'name': k, 'results': sorted(list(v), key=lambda x: x['id'], reverse=True) } for k, v in groupby(sorted(results, key=lambda x: x[group_by]), key=lambda x: x[group_by])] if group_by == 'env_type': ordering = [i.value for i in EnvType] results = sorted(results, key=lambda x: ordering.index(x['name'])) else: results = sorted(results, key=lambda x: x['name'], reverse=True) # 过滤带有ns的集群id cluster_ids_with_ns = [] # 按集群分组时,添加集群环境信息 for r in results: r_ns_list = r.get('results') or [] r_ns = r_ns_list[0] if r_ns_list else {} r['environment'] = r_ns.get('environment', '') r['environment_name'] = get_cluster_env_name( r['environment']) r["cluster_id"] = r_ns.get("cluster_id") if get_cluster_type(r["cluster_id"]) == ClusterType.SHARED: r["is_shared"] = True cluster_ids_with_ns.append(r_ns.get("cluster_id")) # 添加无命名空间集群ID results.extend( self.get_clusters_without_ns(cluster_dict, cluster_ids_with_ns)) else: results = sorted(results, key=lambda x: x['id'], reverse=True) with_perms = str2bool(request.query_params.get('with_perms', True)) if not with_perms: return response.Response(results) namespace_list = [] for namespace in results: namespace['iam_ns_id'] = calc_iam_ns_id(namespace['cluster_id'], namespace['name']) namespace_list.append(namespace) return PermsResponse( namespace_list, NamespaceRequest(project_id=project_id, cluster_id=cluster_id))
def get_service_info(self, request, project_id, cluster_id, namespace, name): # noqa """获取单个 service 的信息""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return APIResponse({"code": 400, "message": _("无法查看共享集群资源")}) access_token = request.user.token.access_token params = { "env": "k8s", "namespace": namespace, "name": name, } client = k8s.K8SClient(access_token, project_id, cluster_id, env=None) resp = client.get_service(params) template_cate = 'k8s' relate_app_cate = 'deployment' if resp.get("code") != ErrorCode.NoError: raise ComponentError(resp.get("message")) resp_data = resp.get("data", []) if not resp_data: return APIResponse({ "code": 400, "message": _("查询不到 Service[{}] 的信息").format(name) }) s_data = resp_data[0].get('data', {}) labels = s_data.get('metadata', {}).get('labels') or {} # 获取命名空间的id namespace_id = app_utils.get_namespace_id(access_token, project_id, (cluster_id, namespace), cluster_id=cluster_id) instance_id = labels.get(LABLE_INSTANCE_ID) # 是否关联LB lb_balance = labels.get('BCSBALANCE') if lb_balance: s_data['isLinkLoadBalance'] = True s_data['metadata']['lb_labels'] = {'BCSBALANCE': lb_balance} else: s_data['isLinkLoadBalance'] = False lb_name = labels.get('BCSGROUP') # 获取模板集信息 template_id = labels.get(LABLE_TEMPLATE_ID) try: lasetest_ver = ShowVersion.objects.filter( template_id=template_id).order_by('-updated').first() show_version_name = lasetest_ver.name version_id = lasetest_ver.real_version_id version_entity = VersionedEntity.objects.get(id=version_id) except Exception: return APIResponse({ "code": 400, "message": _("模板集[id:{}]没有可用的版本,无法更新service").format(template_id) }) entity = version_entity.get_entity() # 获取更新人和创建人 annotations = s_data.get('metadata', {}).get('annotations', {}) creator = annotations.get(ANNOTATIONS_CREATOR, '') updator = annotations.get(ANNOTATIONS_UPDATOR, '') create_time = annotations.get(ANNOTATIONS_CREATE_TIME, '') update_time = annotations.get(ANNOTATIONS_UPDATE_TIME, '') # k8s 更新需要获取版本号 resource_version = s_data.get('metadata', {}).get('resourceVersion') or '' web_cache = annotations.get(ANNOTATIONS_WEB_CACHE) if not web_cache: # 备注中无,则从模板中获取 _services = entity.get('service') if entity else None _services_id_list = _services.split(',') if _services else [] _s = Service.objects.filter(id__in=_services_id_list, name=name).first() try: web_cache = _s.get_config.get('webCache') except Exception: pass else: try: web_cache = json.loads(web_cache) except Exception: pass s_data['webCache'] = web_cache deploy_tag_list = web_cache.get('deploy_tag_list') or [] app_weight = {} # 处理 k8s 中Service的关联数据 if not deploy_tag_list: _servs = entity.get('K8sService') if entity else None _serv_id_list = _servs.split(',') if _servs else [] _k8s_s = K8sService.objects.filter(id__in=_serv_id_list, name=name).first() if _k8s_s: deploy_tag_list = _k8s_s.get_deploy_tag_list() # 标签 和 备注 去除后台自动添加的 or_annotations = s_data.get('metadata', {}).get('annotations', {}) or_labels = s_data.get('metadata', {}).get('labels', {}) if or_labels: pub_keys = PUBLIC_LABELS.keys() show_labels = { key: or_labels[key] for key in or_labels if key not in pub_keys } s_data['metadata']['labels'] = show_labels if or_annotations: pub_an_keys = PUBLIC_ANNOTATIONS.keys() show_annotations = { key: or_annotations[key] for key in or_annotations if key not in pub_an_keys } remove_key(show_annotations, ANNOTATIONS_WEB_CACHE) s_data['metadata']['annotations'] = show_annotations return APIResponse({ "data": { 'service': [{ 'name': name, 'app_id': app_weight.keys(), 'app_weight': app_weight, 'deploy_tag_list': deploy_tag_list, 'config': s_data, 'version': version_id, 'lb_name': lb_name, 'instance_id': instance_id, 'namespace_id': namespace_id, 'cluster_id': cluster_id, 'namespace': namespace, 'creator': creator, 'updator': updator, 'create_time': create_time, 'update_time': update_time, 'show_version_name': show_version_name, 'resource_version': resource_version, 'template_id': template_id, 'template_cate': template_cate, 'relate_app_cate': relate_app_cate, }] } })
def update_resource(self, request, project_id, cluster_id, namespace, name): """更新""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return Response({"code": 400, "message": _("无法操作共享集群资源")}) access_token = request.user.token.access_token if namespace in K8S_SYS_NAMESPACE: return Response({ "code": 400, "message": _("不允许操作系统命名空间[{}]").format(','.join(K8S_SYS_NAMESPACE)), "data": {} }) request_data = request.data or {} request_data['project_id'] = project_id # 验证请求参数 slz = self.slz(data=request.data) slz.is_valid(raise_exception=True) data = slz.data try: config = json.loads(data["config"]) except Exception: config = data["config"] namespace_id = data['namespace_id'] username = request.user.username # 检查是否有命名空间的使用权限 perm_ctx = NamespaceScopedPermCtx(username=username, project_id=project_id, cluster_id=cluster_id, name=namespace) NamespaceScopedPermission().can_use(perm_ctx) # 对配置文件做处理 gparams = { "access_token": access_token, "project_id": project_id, "username": username } generator = GENERATOR_DICT.get(self.cate)(0, namespace_id, **gparams) config = generator.handle_db_config(db_config=config) # 获取上下文信息 context = generator.context now_time = context.get('SYS_UPDATE_TIME') instance_id = data.get('instance_id', 0) context.update({ 'SYS_CREATOR': data.get('creator', ''), 'SYS_CREATE_TIME': data.get('create_time', ''), 'SYS_INSTANCE_ID': instance_id, }) # 生成配置文件 sys_config = copy.deepcopy(self.sys_config) resource_config = update_nested_dict(config, sys_config) resource_config = json.dumps(resource_config) try: config_profile = render_mako_context(resource_config, context) except Exception: logger.exception(u"配置文件变量替换出错\nconfig:%s\ncontext:%s" % (resource_config, context)) raise ValidationError(_("配置文件中有未替换的变量")) config_profile = generator.format_config_profile(config_profile) service_name = config.get('metadata', {}).get('name') _config_content = { 'name': service_name, 'config': json.loads(config_profile), 'context': context } # 更新db中的数据 config_objs = InstanceConfig.objects.filter( namespace=namespace_id, category=self.cate, name=service_name, ) if config_objs.exists(): config_objs.update( creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _instance_config = config_objs.first() else: _instance_config = InstanceConfig.objects.create( namespace=namespace_id, category=self.cate, name=service_name, config=config_profile, instance_id=instance_id, creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _config_content['instance_config_id'] = _instance_config.id configuration = {namespace_id: {self.cate: [_config_content]}} driver = get_scheduler_driver(access_token, project_id, configuration, request.project.kind) result = driver.instantiation(is_update=True) failed = [] if isinstance(result, dict): failed = result.get('failed') or [] # 添加操作审计 activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="instance", resource=service_name, resource_id=_instance_config.id, extra=json.dumps(configuration), description=_("更新{}[{}]命名空间[{}]").format(self.category, service_name, namespace), ).log_modify(activity_status="failed" if failed else "succeed") if failed: return Response({ "code": 400, "message": _("{}[{}]在命名空间[{}]更新失败,请联系集群管理员解决").format( self.category, service_name, namespace), "data": {}, }) return Response({"code": 0, "message": "OK", "data": {}})
def has_permission(self, request, view): cluster_id = view.kwargs.get('cluster_id') or request.query_params.get( 'cluster_id') return get_cluster_type(cluster_id) != ClusterType.SHARED
def list(self, request, project_id, cluster_id): client = Namespace(request.ctx_cluster) response_data = ListApiRespBuilder( client, cluster_type=get_cluster_type(cluster_id), project_code=request.project.english_name ).build() return Response(response_data)
def update_services(self, request, project_id, cluster_id, namespace, name): """更新 service""" if get_cluster_type(cluster_id) == ClusterType.SHARED: return Response({"code": 400, "message": _("无法操作共享集群资源")}) access_token = request.user.token.access_token flag, project_kind = self.get_project_kind(request, project_id) if not flag: return project_kind if namespace in K8S_SYS_NAMESPACE: return Response({ "code": 400, "message": _("不允许操作系统命名空间[{}]").format(','.join(K8S_SYS_NAMESPACE)), "data": {} }) # k8s 相关数据 slz_class = K8sServiceCreateOrUpdateSLZ s_sys_con = K8S_SEVICE_SYS_CONFIG s_cate = 'K8sService' request_data = request.data or {} request_data['version_id'] = request_data['version'] request_data['item_id'] = 0 request_data['project_id'] = project_id show_version_name = request_data.get('show_version_name', '') # 验证请求参数 slz = slz_class(data=request.data) slz.is_valid(raise_exception=True) data = slz.data namespace_id = data['namespace_id'] # 检查是否有命名空间的使用权限 perm_ctx = NamespaceScopedPermCtx(username=request.user.username, project_id=project_id, cluster_id=cluster_id, name=namespace) NamespaceScopedPermission().can_use(perm_ctx) config = json.loads(data['config']) # 获取关联的应用列表 version_id = data['version_id'] version_entity = VersionedEntity.objects.get(id=version_id) logger.exception(f"deploy_tag_list {type(data['deploy_tag_list'])}") handel_k8s_service_db_config(config, data['deploy_tag_list'], version_id, is_upadte=True) resource_version = data['resource_version'] config['metadata']['resourceVersion'] = resource_version cluster_version = get_cluster_version(access_token, project_id, cluster_id) config = handle_k8s_api_version(config, cluster_id, cluster_version, 'Service') # 前端的缓存数据储存到备注中 config = handle_webcache_config(config) # 获取上下文信息 username = request.user.username now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') context = { 'SYS_CLUSTER_ID': cluster_id, 'SYS_NAMESPACE': namespace, 'SYS_VERSION_ID': version_id, 'SYS_PROJECT_ID': project_id, 'SYS_OPERATOR': username, 'SYS_TEMPLATE_ID': version_entity.template_id, 'SYS_VERSION': show_version_name, 'LABLE_VERSION': show_version_name, 'SYS_INSTANCE_ID': data['instance_id'], 'SYS_CREATOR': data.get('creator', ''), 'SYS_CREATE_TIME': data.get('create_time', ''), 'SYS_UPDATOR': username, 'SYS_UPDATE_TIME': now_time, } bcs_context = get_bcs_context(access_token, project_id) context.update(bcs_context) # 生成配置文件 sys_config = copy.deepcopy(s_sys_con) resource_config = update_nested_dict(config, sys_config) resource_config = json.dumps(resource_config) try: config_profile = render_mako_context(resource_config, context) except Exception: logger.exception(u"配置文件变量替换出错\nconfig:%s\ncontext:%s" % (resource_config, context)) raise ValidationError(_("配置文件中有未替换的变量")) service_name = config.get('metadata', {}).get('name') _config_content = { 'name': service_name, 'config': json.loads(config_profile), 'context': context } # 更新 service config_objs = InstanceConfig.objects.filter( namespace=namespace_id, category=s_cate, name=service_name, ) if config_objs.exists(): config_objs.update( creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _instance_config = config_objs.first() else: _instance_config = InstanceConfig.objects.create( namespace=namespace_id, category=s_cate, name=service_name, config=config_profile, instance_id=data.get('instance_id', 0), creator=username, updator=username, oper_type='update', updated=now_time, is_deleted=False, ) _config_content['instance_config_id'] = _instance_config.id configuration = {namespace_id: {s_cate: [_config_content]}} driver = get_scheduler_driver(access_token, project_id, configuration, request.project.kind) result = driver.instantiation(is_update=True) failed = [] if isinstance(result, dict): failed = result.get('failed') or [] # 添加操作审计 activity_client.ContextActivityLogClient( project_id=project_id, user=username, resource_type="instance", resource=service_name, resource_id=_instance_config.id, extra=json.dumps(configuration), description=_("更新Service[{}]命名空间[{}]").format( service_name, namespace), ).log_modify(activity_status="failed" if failed else "succeed") if failed: return Response({ "code": 400, "message": _("Service[{}]在命名空间[{}]更新失败,请联系集群管理员解决").format( service_name, namespace), "data": {}, }) return Response({"code": 0, "message": "OK", "data": {}})