# references: # - https://json-schema.org/draft/2019-09/json-schema-core.html # - https://json-schema.org/draft/2019-09/json-schema-validation.html from copy import deepcopy import jsonschema import requests def validate_response(resp: requests.Response, schema=None): if schema: jsonschema.validate( resp.json(), schema, format_checker=jsonschema.draft7_format_checker) return resp class _type_cls(dict): def __init__(self, t): dict.__init__(self, type=t) def __call__(self, **props): o = deepcopy(self) o.update(props) return o class types: str = _type_cls('string') num = _type_cls('number') int = _type_cls('integer') null = _type_cls('null') bool = int(enum=[0, 1]) @staticmethod def array(t, min_items=None, max_items=None): arr = {'type': 'array', 'items': t} if isinstance(min_items, int): assert min_items >= 0 arr['minItems'] = min_items if isinstance(max_items, int): assert max_items >= 0 arr['maxItems'] = max_items if 'minItems' in arr and 'maxItems' in arr: assert min_items <= max_items return arr @staticmethod def any_of(*ts): return {'anyOf': list(ts)} @staticmethod def all_of(*ts): return {'allOf': list(ts)} @staticmethod def one_of(*ts): return {'oneOf': list(ts)} def resource_template(): return { 'type': 'object', 'properties': { 'cpu': types.int(), 'memory': types.int() }, 'required': ['cpu', 'memory'], } def platform_settings(): return { 'type': 'object', 'properties': { 'azs': types.array(types.str), 'tidbVersions': types.array(types.str), 'replicationFactors': types.array(types.int(minimum=0)), 'pdResourceTemplates': types.array(resource_template()), 'tikvResourceTemplates': types.array(resource_template()), 'tidbResourceTemplates': types.array(resource_template()), }, 'required': [ 'azs', 'tidbVersions', 'replicationFactors', 'pdResourceTemplates', 'tikvResourceTemplates', 'tidbResourceTemplates', ], } def platform_info(): return { 'type': 'object', 'properties': { 'kubernetesVersion': types.str, 'tidbOperatorVersion': types.str, 'metadbVersion': types.str, 'operationTime': types.str(format='date-time'), }, 'required': [ 'kubernetesVersion', 'tidbOperatorVersion', 'metadbVersion', 'operationTime', ] } def clusters_list(iterms_perpage): return { "type": "object", "properties": { "apiVersion": types.str(format="uri"), "items": types.array(cluster_info(), max_items=iterms_perpage), "kind": types.str, "metadata": { "type": "object", "properties": { "selfLink": types.str } }, "total": types.int } } def cluster_metadata(): return { "type": "object", "properties": { "creationTimestamp": types.one_of( types.str(format="date-time"), types.null), "name": types.str(pattern="^[a-z][a-z0-9]{15}$"), "selfLink": types.str(format="uri") } } def cluster_monitor(): return {"type": "object", "properties": {"storageClassName": types.str}} def cluster_info(): return { "type": "object", "properties": { "azs": types.array(types.str), "clusterName": types.str(pattern="^[a-zA-Z][a-zA-Z0-9-]{7,31}$"), "endpoints": types.array(types.str(format="ipv4")), "metadata": cluster_metadata(), "monitor": cluster_monitor(), "port": types.int(minimum=0, maximum=65535), "replicationFactor": types.int(enmu=[3, 5, 7]), "status": types.str(enum=[ "creating", "upgrading", "scaling", "running", "failed", "unhealthy", "recovering" ]), "tags": types.one_of( types.array(types.str, min_items=0, max_items=20), types.null), "pd": pd_info(), "tidb": tidb_info(), "tikv": tikv_info(), "version": types.str }, "required": [ "azs", "clusterName", "endpoints", "metadata", "monitor", "pd", "port", "replicationFactor", "status", "tidb", "tikv", "version" ] } def new_cluster_info(): return { "type": "object", "properties": { "kind": types.str, "apiVersion": types.str, "password": types.str, "azs": types.array(types.str), "clusterName": types.str(pattern="^[a-zA-Z][a-zA-Z0-9-]{7,31}$"), "endpoints": types.null, "metadata": cluster_metadata(), "monitor": cluster_monitor(), "port": types.int(minimum=0, maximum=65535), "replicationFactor": types.int(enmu=[3, 5, 7]), "status": types.str, "tags": types.one_of( types.array(types.str, min_items=0, max_items=20), types.null), "pd": pd_info(), "tidb": tidb_info(), "tikv": tikv_info(), "version": types.str }, "required": [ "kind", "apiVersion", "password", "azs", "clusterName", "endpoints", "metadata", "monitor", "pd", "port", "replicationFactor", "status", "tidb", "tikv", "version" ] } def pd_info(): return { "type": "object", "properties": { "replicas": types.int(enum=[3, 5, 7]), "resourceTemplate": resource_template(), "storageClassName": types.str } } def tidb_info(): return { "type": "object", "properties": { "replicas": types.int(minimum=1), "resourceTemplate": resource_template(), } } def tikv_info(): return { "type": "object", "properties": { "replicas": types.int(minimum=3), "resourceTemplate": resource_template(), "storageClassName": types.str } } def failure_info(): return { 'type': 'object', 'properties': { 'status': { 'type': 'string', 'enum': ['Failure'] } }, 'required': ['status'] } def tidb_list_info(iterms_perpage): return { "type": "object", "properties": { "apiVersion": types.str, "items": types.array(new_tidb_info(), max_items=iterms_perpage), "kind": types.str, "metadata": { "type": "object", "properties": { "selfLink": types.str } }, "total": types.int } } def new_tidb_info(): return { "type": "object", "properties": { "az": types.str, "health": {"type": "boolean"}, "metadata": tidb_meta_data(), "nodeName": types.str(format="uri"), "podPhase": types.str(enum=[ "pending", "running", "succeeded", "failed", "unknown" ]), "resourceTemplate": resource_template(), "version": types.str }, "required": [ "az", "health", "metadata", "nodeName", "podPhase", "resourceTemplate", "version" ], } def tidb_meta_data(): return { "type": "object", "properties": { "creationTimestamp": types.one_of( types.str(format="date-time"), types.null), "name": types.str(pattern="^[a-z][a-z0-9]{15}-tidb-[0-9]+$"), "selfLink": types.str(format="uri") } } def tikv_list_info(iterms_perpage): return { "type": "object", "properties": { "apiVersion": types.str, "items": types.array(new_tikv_info(), max_items=iterms_perpage), "kind": types.str, "metadata": { "type": "object", "properties": { "selfLink": types.str } }, "total": types.int } } def new_tikv_info(): return { "type": "object", "properties": { "az": types.str, "state": types.str, "metadata": tikv_meta_data(), "nodeName": types.str(format="uri"), "podPhase": types.str(enum=[ "pending", "running", "succeeded", "failed", "unknown" ]), "resourceTemplate": resource_template(), "version": types.str }, "required": [ "az", "state", "metadata", "nodeName", "podPhase", "resourceTemplate", "version" ], } def tikv_meta_data(): return { "type": "object", "properties": { "creationTimestamp": types.one_of( types.str(format="date-time"), types.null), "name": types.str(pattern="^[a-z][a-z0-9]{15}-tikv-[0-9]+$"), "selfLink": types.str(format="uri") } } def pd_list_info(iterms_perpage): return { "type": "object", "properties": { "apiVersion": types.str, "items": types.array(new_pd_info(), max_items=iterms_perpage), "kind": types.str, "metadata": { "type": "object", "properties": { "selfLink": types.str } }, "total": types.int } } def new_pd_info(): return { "type": "object", "properties": { "az": types.str, "role": types.str(enum=["leader", "follower"]), "health": {"type": "boolean"}, "metadata": pd_meta_data(), "nodeName": types.str(format="uri"), "podPhase": types.str(enum=[ "pending", "running", "succeeded", "failed", "unknown" ]), "resourceTemplate": resource_template(), "version": types.str }, "required": [ "az", "role", "metadata", "nodeName", "podPhase", "health", "resourceTemplate", "version" ], } def pd_meta_data(): return { "type": "object", "properties": { "creationTimestamp": types.one_of( types.str(format="date-time"), types.null), "name": types.str(pattern="^[a-z][a-z0-9]{15}-pd-[0-9]+$"), "selfLink": types.str(format="uri") } } def failed_cluster_info(iterms_perpage): return { "type": "object", "properties": { "apiVersion": types.str, "kind": types.str, "metadata": { "type": "object", "properties": { "selfLink": types.str } }, "items": { "type": "array", "max_items": iterms_perpage, "items": { "type": "object", "required": ["metadata", "clusterName", "tags", "reasons"], "properties": { "metadata": cluster_metadata(), "clusterName": types.str(pattern="^[a-zA-Z][a-zA-Z0-9-]{7,31}$"), "tags": types.one_of( types.array(types.str, min_items=0, max_items=20), types.null), "reasons": types.array(types.str) } } } }, "total": types.int }