From 1c97afed7dbe81e2474598ed20336e7ec70c694b Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Tue, 21 Oct 2025 23:00:48 +0800 Subject: [PATCH] init datamate --- .editorconfig | 21 + .github/workflows/docker-image-backend.yml | 27 + .github/workflows/docker-image-frontend.yml | 27 + .gitignore | 189 + LICENSE | 20 + Makefile | 164 + README-zh.md | 72 + README.md | 77 + backend/api-gateway/pom.xml | 55 + .../gateway/ApiGatewayApplication.java | 77 + backend/openapi/README.md | 147 + backend/openapi/specs/data-annotation.yaml | 298 + backend/openapi/specs/data-cleaning.yaml | 491 + backend/openapi/specs/data-collection.yaml | 517 + backend/openapi/specs/data-evaluation.yaml | 630 + backend/openapi/specs/data-management.yaml | 719 + backend/openapi/specs/data-synthesis.yaml | 620 + backend/openapi/specs/execution-engine.yaml | 712 + backend/openapi/specs/operator-market.yaml | 547 + .../openapi/specs/pipeline-orchestration.yaml | 639 + backend/pom.xml | 212 + .../services/data-annotation-service/pom.xml | 101 + .../services/data-cleaning-service/img.png | Bin 0 -> 137462 bytes .../services/data-cleaning-service/img1.png | Bin 0 -> 48601 bytes .../services/data-cleaning-service/img2.png | Bin 0 -> 92852 bytes .../services/data-cleaning-service/pom.xml | 87 + .../DataCleaningServiceConfiguration.java | 22 + .../application/httpclient/DatasetClient.java | 120 + .../application/httpclient/RuntimeClient.java | 54 + .../scheduler/CleaningTaskScheduler.java | 40 + .../service/CleaningTaskService.java | 186 + .../service/CleaningTemplateService.java | 95 + .../converter/OperatorInstanceConverter.java | 33 + .../domain/model/CreateDatasetRequest.java | 26 + .../domain/model/DatasetFileResponse.java | 36 + .../domain/model/DatasetResponse.java | 44 + .../domain/model/DatasetTypeResponse.java | 23 + .../cleaning/domain/model/ExecutorType.java | 25 + .../domain/model/OperatorInstancePo.java | 13 + .../model/PagedDatasetFileResponse.java | 28 + .../cleaning/domain/model/TaskProcess.java | 24 + .../domain/model/TemplateWithInstance.java | 30 + .../exception/CleanErrorCode.java | 19 + .../mapper/CleaningResultMapper.java | 9 + .../mapper/CleaningTaskMapper.java | 21 + .../mapper/CleaningTemplateMapper.java | 25 + .../mapper/OperatorInstanceMapper.java | 17 + .../api/CleaningTaskController.java | 59 + .../api/CleaningTemplateController.java | 74 + .../interfaces/dto/CleaningProcess.java | 20 + .../cleaning/interfaces/dto/CleaningTask.java | 92 + .../interfaces/dto/CleaningTemplate.java | 33 + .../dto/CreateCleaningTaskRequest.java | 32 + .../dto/CreateCleaningTemplateRequest.java | 23 + .../interfaces/dto/OperatorInstance.java | 22 + .../interfaces/dto/OperatorResponse.java | 41 + .../dto/UpdateCleaningTemplateRequest.java | 26 + .../mappers/CleaningResultMapper.xml | 8 + .../resources/mappers/CleaningTaskMapper.xml | 56 + .../mappers/CleaningTemplateMapper.xml | 38 + .../mappers/OperatorInstanceMapper.xml | 16 + .../data-collection-service/README.md | 229 + .../data-collection-service/image.png | Bin 0 -> 80705 bytes .../data-collection-service/image1.png | Bin 0 -> 53113 bytes .../data-collection-service/image2.png | Bin 0 -> 68926 bytes .../data-collection-service/image3.png | Bin 0 -> 109289 bytes .../services/data-collection-service/pom.xml | 200 + .../DataCollectionServiceConfiguration.java | 24 + .../scheduler/TaskSchedulerInitializer.java | 66 + .../service/CollectionTaskService.java | 85 + .../service/DataxExecutionService.java | 60 + .../service/TaskExecutionService.java | 83 + .../domain/model/CollectionTask.java | 45 + .../domain/model/DataxTemplate.java | 71 + .../domain/model/TaskExecution.java | 39 + .../collection/domain/model/TaskStatus.java | 21 + .../mapper/CollectionTaskMapper.java | 47 + .../mapper/TaskExecutionMapper.java | 38 + .../typehandler/TaskStatusTypeHandler.java | 0 .../runtime/datax/DataxJobBuilder.java | 83 + .../runtime/datax/DataxProcessRunner.java | 46 + .../runtime/datax/DataxProperties.java | 17 + .../converter/CollectionTaskConverter.java | 52 + .../rest/CollectionTaskController.java | 83 + .../rest/TaskExecutionController.java | 101 + .../config/application-datacollection.yml | 23 + .../mappers/CollectionTaskMapper.xml | 188 + .../resources/mappers/TaskExecutionMapper.xml | 191 + .../services/data-evaluation-service/pom.xml | 92 + .../services/data-management-service/pom.xml | 113 + .../DataManagementServiceConfiguration.java | 22 + .../DatasetApplicationService.java | 288 + .../DatasetFileApplicationService.java | 306 + .../application/FileMetadataService.java | 127 + .../application/TagApplicationService.java | 116 + .../common/enums/DatasetStatusType.java | 41 + .../common/enums/DatasetType.java | 28 + .../domain/contants/DatasetConstant.java | 11 + .../domain/model/dataset/Dataset.java | 146 + .../domain/model/dataset/DatasetFile.java | 35 + .../dataset/DatasetFileUploadCheckInfo.java | 18 + .../domain/model/dataset/StatusConstants.java | 33 + .../domain/model/dataset/Tag.java | 33 + .../client/CollectionTaskClient.java | 22 + .../dto/CollectionTaskDetailResponse.java | 23 + .../client/dto/LocalCollectionConfig.java | 21 + .../config/DataManagementConfig.java | 37 + .../config/DataManagementProperties.java | 82 + .../exception/DataManagementErrorCode.java | 39 + .../persistence/mapper/DatasetFileMapper.java | 30 + .../persistence/mapper/DatasetMapper.java | 33 + .../persistence/mapper/TagMapper.java | 27 + .../repository/DatasetFileRepository.java | 27 + .../repository/DatasetRepository.java | 29 + .../impl/DatasetFileRepositoryImpl.java | 54 + .../impl/DatasetRepositoryImpl.java | 73 + .../converter/DatasetConverter.java | 53 + .../interfaces/converter/TagConverter.java | 30 + .../dto/AllDatasetStatisticsResponse.java | 20 + .../interfaces/dto/CreateDatasetRequest.java | 35 + .../interfaces/dto/CreateTagRequest.java | 18 + .../interfaces/dto/DatasetFileResponse.java | 36 + .../interfaces/dto/DatasetPagingQuery.java | 42 + .../interfaces/dto/DatasetResponse.java | 47 + .../dto/DatasetStatisticsResponse.java | 26 + .../interfaces/dto/DatasetTypeResponse.java | 24 + .../dto/PagedDatasetFileResponse.java | 28 + .../interfaces/dto/PagedDatasetResponse.java | 28 + .../interfaces/dto/TagResponse.java | 22 + .../interfaces/dto/UpdateDatasetRequest.java | 25 + .../interfaces/dto/UpdateTagRequest.java | 20 + .../interfaces/dto/UploadFileRequest.java | 34 + .../interfaces/dto/UploadFilesPreRequest.java | 22 + .../interfaces/rest/DatasetController.java | 115 + .../rest/DatasetFileController.java | 163 + .../rest/DatasetTypeController.java | 53 + .../interfaces/rest/TagController.java | 85 + .../config/application-datamanagement.yml | 11 + .../resources/mappers/DatasetFileMapper.xml | 98 + .../main/resources/mappers/DatasetMapper.xml | 152 + .../src/main/resources/mappers/TagMapper.xml | 111 + .../services/data-synthesis-service/pom.xml | 92 + .../services/execution-engine-service/pom.xml | 96 + backend/services/main-application/pom.xml | 169 + .../main/DataMatePlatformApplication.java | 49 + .../datamate/main/config/SecurityConfig.java | 26 + .../src/main/resources/application.yml | 179 + .../config/application-datacollection.yml | 23 + .../config/application-datamanagement.yml | 11 + .../src/main/resources/log4j2.xml | 42 + .../services/operator-market-service/img.png | Bin 0 -> 146785 bytes .../operator-market-service/img_1.png | Bin 0 -> 45904 bytes .../services/operator-market-service/pom.xml | 94 + .../OperatorMarketServiceConfiguration.java | 24 + .../operator/application/CategoryService.java | 62 + .../operator/application/LabelService.java | 22 + .../operator/application/OperatorService.java | 76 + .../domain/converter/OperatorConverter.java | 28 + .../operator/domain/modal/Category.java | 14 + .../domain/modal/CategoryRelation.java | 12 + .../operator/domain/modal/Operator.java | 35 + .../domain/modal/RelationCategoryDTO.java | 15 + .../persistence/mapper/CategoryMapper.java | 12 + .../mapper/CategoryRelationMapper.java | 18 + .../persistence/mapper/OperatorMapper.java | 27 + .../interfaces/api/CategoryController.java | 27 + .../interfaces/api/LabelController.java | 40 + .../interfaces/api/OperatorController.java | 54 + .../interfaces/dto/CategoryTreeResponse.java | 22 + .../interfaces/dto/CreateOperatorRequest.java | 36 + .../operator/interfaces/dto/Label.java | 13 + .../interfaces/dto/OperatorResponse.java | 43 + .../dto/OperatorsListPostRequest.java | 30 + .../operator/interfaces/dto/SubCategory.java | 18 + .../interfaces/dto/UpdateOperatorRequest.java | 32 + .../main/resources/mappers/CategoryMapper.xml | 8 + .../mappers/CategoryRelationMapper.xml | 24 + .../main/resources/mappers/OperatorMapper.xml | 96 + .../pipeline-orchestration-service/pom.xml | 96 + backend/services/rag-indexer-service/pom.xml | 96 + .../rag/indexer/RagApplication.java | 17 + backend/services/rag-query-service/pom.xml | 72 + backend/shared/domain-common/pom.xml | 37 + .../datamate/common/domain/AggregateRoot.java | 27 + .../datamate/common/domain/ValueObject.java | 16 + .../domain/model/ChunkUploadPreRequest.java | 49 + .../domain/model/ChunkUploadRequest.java | 36 + .../common/domain/model/FileUploadResult.java | 24 + .../common/domain/model/UploadCheckInfo.java | 7 + .../common/domain/model/base/BaseEntity.java | 61 + .../common/domain/service/FileService.java | 91 + .../common/domain/utils/AnalyzerUtils.java | 40 + .../common/domain/utils/ChunksSaver.java | 134 + .../common/domain/utils/CommonUtils.java | 24 + .../common/IgnoreResponseWrap.java | 15 + .../infrastructure/common/Response.java | 63 + .../config/EntityMetaObjectHandler.java | 60 + .../config/GlobalExceptionHandler.java | 54 + .../config/GlobalResponseHandler.java | 77 + .../config/MybatisPlusConfig.java | 47 + .../exception/BusinessAssert.java | 35 + .../exception/BusinessException.java | 73 + .../infrastructure/exception/ErrorCode.java | 23 + .../exception/ErrorCodeImpl.java | 15 + .../exception/SystemErrorCode.java | 43 + .../mapper/ChunkUploadRequestMapper.java | 49 + .../common/interfaces/PagedResponse.java | 44 + .../common/interfaces/PagingQuery.java | 22 + .../mappers/ChunkUploadRequestMapper.xml | 65 + backend/shared/security-common/pom.xml | 46 + .../datamate/common/security/JwtUtils.java | 106 + deployment/docker/datamate/docker-compose.yml | 86 + deployment/docker/datamate/utf8.cnf | 15 + .../helm/ray/kuberay-operator/Chart.yaml | 24 + .../crds/ray.io_rayclusters.yaml | 16101 +++++++++++ .../kuberay-operator/crds/ray.io_rayjobs.yaml | 23549 ++++++++++++++++ .../crds/ray.io_rayservices.yaml | 16805 +++++++++++ .../kuberay-operator/templates/_helpers.tpl | 322 + .../templates/deployment.yaml | 150 + .../templates/leader_election_role.yaml | 37 + .../leader_election_role_binding.yaml | 17 + .../templates/multiple_namespaces_role.yaml | 13 + .../multiple_namespaces_rolebinding.yaml | 20 + .../templates/ray_rayjob_editor_role.yaml | 28 + .../templates/ray_rayjob_viewer_role.yaml | 24 + .../templates/ray_rayservice_editor_role.yaml | 28 + .../templates/ray_rayservice_viewer_role.yaml | 24 + .../ray/kuberay-operator/templates/role.yaml | 9 + .../templates/rolebinding.yaml | 16 + .../kuberay-operator/templates/service.yaml | 17 + .../templates/serviceaccount.yaml | 9 + .../templates/servicemonitor.yaml | 23 + .../helm/ray/kuberay-operator/values.yaml | 221 + deployment/helm/ray/ray-cluster/Chart.yaml | 5 + .../ray/ray-cluster/templates/_helpers.tpl | 55 + .../templates/raycluster-cluster.yaml | 407 + deployment/helm/ray/ray-cluster/values.yaml | 396 + deployment/helm/ray/service.yaml | 15 + deployment/kubernetes/backend/deploy.yaml | 120 + deployment/kubernetes/datax/deploy.yaml | 54 + deployment/kubernetes/frontend/deploy.yaml | 38 + deployment/kubernetes/mineru/deploy.yaml | 52 + deployment/kubernetes/mysql/configmap.yaml | 21 + deployment/kubernetes/mysql/deploy.yaml | 61 + .../kubernetes/unstructured/deploy.yaml | 49 + editions/community/config/application.yml | 181 + editions/community/config/log4j2.xml | 42 + editions/enterprise/config/application.yml | 181 + editions/enterprise/config/log4j2.xml | 42 + frontend/.gitignore | 28 + frontend/README.md | 96 + frontend/eslint.config.js | 23 + frontend/index.html | 13 + frontend/package-lock.json | 7125 +++++ frontend/package.json | 49 + frontend/public/huawei-logo.webp | Bin 0 -> 19172 bytes frontend/src/components/AddTagPopover.tsx | 129 + frontend/src/components/CardView.tsx | 291 + frontend/src/components/DetailHeader.tsx | 137 + .../src/components/DevelopmentInProgress.tsx | 27 + frontend/src/components/ErrorBoundary.tsx | 191 + frontend/src/components/RadioCard.tsx | 70 + frontend/src/components/SearchControls.tsx | 239 + frontend/src/components/TagList.tsx | 149 + frontend/src/components/TagManagement.tsx | 271 + frontend/src/components/TaskPopover.tsx | 162 + frontend/src/components/TopLoadingBar.tsx | 69 + frontend/src/hooks/useDebouncedEffect.ts | 17 + frontend/src/hooks/useFetchData.ts | 113 + frontend/src/hooks/useLeavePrompt.ts | 52 + frontend/src/hooks/useSearchParams.tsx | 18 + frontend/src/hooks/useStyle.ts | 20 + frontend/src/index.css | 45 + frontend/src/main.tsx | 18 + frontend/src/mock/annotation.tsx | 330 + frontend/src/mock/cleansing.tsx | 56 + frontend/src/mock/evaluation.tsx | 290 + frontend/src/mock/knowledgeBase.tsx | 254 + frontend/src/mock/mock-apis.cjs | 149 + frontend/src/mock/mock-core/module-loader.cjs | 25 + .../src/mock/mock-core/session-helper.cjs | 63 + frontend/src/mock/mock-core/util.cjs | 30 + .../error-handle-middleware.cjs | 13 + frontend/src/mock/mock-middleware/index.cjs | 11 + .../mock-middleware/send-json-middleawre.cjs | 18 + .../mock-middleware/set-header-middleware.cjs | 14 + .../strong-match-middleware.cjs | 13 + .../src/mock/mock-seed/data-annotation.cjs | 618 + .../src/mock/mock-seed/data-cleansing.cjs | 544 + .../src/mock/mock-seed/data-collection.cjs | 231 + .../src/mock/mock-seed/data-evaluation.cjs | 501 + .../src/mock/mock-seed/data-management.cjs | 437 + .../src/mock/mock-seed/data-synthesis.cjs | 522 + .../mock/mock-seed/knowledge-generation.cjs | 0 .../src/mock/mock-seed/operator-market.cjs | 124 + frontend/src/mock/mock.cjs | 58 + frontend/src/mock/nodemon.json | 22 + frontend/src/mock/operator.tsx | 196 + frontend/src/mock/ratio.tsx | 193 + frontend/src/mock/synthesis.tsx | 209 + frontend/src/pages/Agent/Agent.tsx | 480 + .../Annotate/AnnotationWorkSpace.tsx | 229 + .../Annotate/components/AudioAnnotation.tsx | 713 + .../Annotate/components/ImageAnnotation.tsx | 617 + .../Annotate/components/TextAnnotation.tsx | 457 + .../Annotate/components/VideoAnnotation.tsx | 688 + .../DataAnnotation/Create/CreateTask.tsx | 346 + .../components/CreateAnnptationTaskDialog.tsx | 98 + .../components/CustomTemplateDialog.tsx | 225 + .../DataAnnotation/Detail/TaskDetail.tsx | 0 .../DataAnnotation/Home/DataAnnotation.tsx | 181 + .../pages/DataAnnotation/annotation.api.ts | 262 + .../pages/DataAnnotation/annotation.const.tsx | 56 + .../pages/DataAnnotation/annotation.model.ts | 27 + .../pages/DataCleansing/Create/CreateTask.tsx | 131 + .../DataCleansing/Create/CreateTempate.tsx | 119 + .../pages/DataCleansing/Create/DragDrop.css | 411 + .../DataCleansing/Create/DragExample.tsx | 430 + .../Create/components/CreateTaskStepOne.tsx | 119 + .../components/CreateTemplateStepOne.tsx | 38 + .../Create/components/OperatorConfig.tsx | 81 + .../Create/components/OperatorLibrary.tsx | 282 + .../components/OperatorOrchestration.tsx | 207 + .../Create/components/ParamConfig.tsx | 234 + .../Create/hooks/useCreateStepTwo.tsx | 86 + .../Create/hooks/useDragOperators.ts | 158 + .../Create/hooks/useOperatorOperations.ts | 155 + .../pages/DataCleansing/Detail/TaskDetail.tsx | 176 + .../Detail/components/BasicInfo.tsx | 148 + .../Detail/components/FileTable.tsx | 503 + .../Detail/components/LogsTable.tsx | 110 + .../Detail/components/OperatorTable.tsx | 103 + .../DataCleansing/Home/DataCleansing.tsx | 61 + .../Home/components/ProcessFlowDiagram.tsx | 86 + .../Home/components/TaskList.tsx | 257 + .../Home/components/TemplateList.tsx | 46 + .../src/pages/DataCleansing/cleansing.api.ts | 57 + .../pages/DataCleansing/cleansing.const.tsx | 134 + .../pages/DataCleansing/cleansing.model.ts | 68 + .../DataCollection/Create/CreateTask.tsx | 359 + .../DataCollection/Home/DataCollection.tsx | 44 + .../Home/components/ExecutionLog.tsx | 153 + .../Home/components/TaskManagement.tsx | 200 + .../pages/DataCollection/collection.apis.ts | 60 + .../pages/DataCollection/collection.const.ts | 69 + .../pages/DataCollection/collection.model.ts | 52 + .../DataEvaluation/Create/CreateTask.tsx | 574 + .../Evaluate/ManualEvaluate.tsx | 407 + .../DataEvaluation/Home/DataEvaluation.tsx | 484 + .../Report/EvaluationReport.tsx | 310 + .../DataEvaluation/data-evaluation.api.ts | 243 + .../pages/DataEvaluation/data-evaluation.d.ts | 73 + .../DataManagement/Create/CreateDataset.tsx | 79 + .../DataManagement/Create/EditDataset.tsx | 101 + .../Create/components/BasicInformation.tsx | 94 + .../DataManagement/Detail/DatasetDetail.tsx | 227 + .../Detail/components/DataLineageFlow.tsx | 52 + .../Detail/components/DataQuality.tsx | 77 + .../Detail/components/ImportConfiguration.tsx | 235 + .../Detail/components/Overview.tsx | 211 + .../DataManagement/Home/DataManagement.tsx | 350 + .../src/pages/DataManagement/dataset.api.ts | 198 + .../pages/DataManagement/dataset.const.tsx | 218 + .../src/pages/DataManagement/dataset.model.ts | 100 + .../src/pages/DataManagement/hooks/index.ts | 2 + .../DataManagement/hooks/useFilesOperation.ts | 124 + .../DataManagement/hooks/useImportFile.tsx | 61 + frontend/src/pages/Home/Home.tsx | 314 + .../Create/KnowledgeBaseCreate.tsx | 671 + .../Detail/KnowledgeBaseDetail.tsx | 672 + .../FileDetail/KnowledgeBaseFileDetail.tsx | 695 + .../Home/KnowledgeGeneration.tsx | 409 + .../KnowledgeGeneration/knowledge-base.api.ts | 0 .../knowledge-base.const.ts | 0 .../knowledge-base.model.ts | 89 + frontend/src/pages/Layout/MainLayout.tsx | 23 + frontend/src/pages/Layout/Sidebar.tsx | 183 + frontend/src/pages/Layout/TaskUpload.tsx | 217 + frontend/src/pages/Layout/menu.tsx | 115 + .../Create/OperatorPluginCreate.tsx | 204 + .../Create/components/ConfigureStep.tsx | 275 + .../Create/components/ParsingStep.tsx | 50 + .../Create/components/PreviewStep.tsx | 25 + .../Create/components/UploadStep.tsx | 84 + .../Detail/OperatorPluginDetail.tsx | 356 + .../Detail/components/ChangeLog.tsx | 34 + .../Detail/components/Documentation.tsx | 15 + .../Detail/components/Examples.tsx | 28 + .../Detail/components/Install.tsx | 105 + .../Detail/components/Overview.tsx | 167 + .../Detail/components/Reviews.tsx | 88 + .../OperatorMarket/Home/OperatorMarket.tsx | 181 + .../Home/components/Filters.tsx | 179 + .../OperatorMarket/Home/components/List.tsx | 150 + .../OperatorMarket/OperatorPluginEdit.tsx | 26 + .../src/pages/OperatorMarket/operator.api.ts | 243 + .../pages/OperatorMarket/operator.const.tsx | 9 + .../pages/OperatorMarket/operator.model.ts | 55 + .../src/pages/Orchestration/Orchestration.tsx | 842 + .../pages/Orchestration/WorkflowEditor.tsx | 462 + .../Orchestration/components/CustomNode.tsx | 159 + .../src/pages/RatioTask/CreateRatioTask.tsx | 571 + frontend/src/pages/RatioTask/RatioTask.tsx | 382 + frontend/src/pages/RatioTask/ratio.d.ts | 24 + frontend/src/pages/SettingsPage/Settings.tsx | 66 + .../components/EnvironmentAccess.tsx | 365 + .../SettingsPage/components/SystemConfig.tsx | 86 + .../SettingsPage/components/WebhookConfig.tsx | 313 + .../src/pages/SynthesisTask/CreateTask.tsx | 1250 + .../pages/SynthesisTask/CreateTemplate.tsx | 302 + .../src/pages/SynthesisTask/DataSynthesis.tsx | 84 + .../components/InstructionTemplateTab.tsx | 214 + .../components/SynthesisTaskTab.tsx | 361 + .../src/pages/SynthesisTask/synthesis.d.ts | 35 + .../pages/TaskManagement/TaskManagement.tsx | 0 frontend/src/pages/globals.css | 97 + frontend/src/providers/MultiProvider.js | 29 + frontend/src/routes/routes.ts | 268 + frontend/src/utils/file.util.ts | 79 + frontend/src/utils/loading.ts | 55 + frontend/src/utils/request.ts | 526 + frontend/src/utils/unit.ts | 295 + frontend/src/vite-env.d.ts | 1 + frontend/tailwind.config.ts | 76 + frontend/tsconfig.app.json | 32 + frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 25 + frontend/vite.config.ts | 42 + runtime/datax/nfsreader/pom.xml | 80 + .../nfsreader/src/main/assembly/package.xml | 35 + .../plugin/reader/nfsreader/MountUtil.java | 121 + .../plugin/reader/nfsreader/NfsReader.java | 112 + .../nfsreader/src/main/resources/plugin.json | 6 + .../main/resources/plugin_job_template.json | 7 + runtime/datax/nfswriter/pom.xml | 77 + .../nfswriter/src/main/assembly/package.xml | 35 + .../plugin/writer/nfswriter/MountUtil.java | 121 + .../plugin/writer/nfswriter/NfsWriter.java | 100 + .../plugin/writer/nfswriter/ShellUtil.java | 43 + .../nfswriter/src/main/resources/plugin.json | 6 + .../main/resources/plugin_job_template.json | 8 + runtime/datax/package.xml | 585 + runtime/datax/pom.xml | 308 + runtime/ops/README.md | 89 + .../examples/text_length_filter/metadata.json | 49 + .../examples/text_length_filter/operator.py | 135 + runtime/ops/filter/__init__.py | 29 + .../__init__.py | 6 + .../metadata.yml | 31 + .../process.py | 73 + .../resources/hit_stopwords.txt | 1039 + .../__init__.py | 6 + .../metadata.yml | 25 + .../process.py | 51 + .../__init__.py | 6 + .../metadata.yml | 25 + .../process.py | 49 + .../resources/special_token.txt | 50 + .../__init__.py | 6 + .../metadata.yml | 16 + .../process.py | 127 + .../wechat_qrcode_model.py | 23 + .../img_blurred_images_cleaner/__init__.py | 6 + .../img_blurred_images_cleaner/metadata.yml | 25 + .../img_blurred_images_cleaner/process.py | 50 + .../img_duplicated_images_cleaner/__init__.py | 6 + .../metadata.yml | 16 + .../img_duplicated_images_cleaner/process.py | 109 + .../sql/sql_config.json | 5 + .../img_similar_images_cleaner/__init__.py | 6 + .../img_similar_images_cleaner/metadata.yml | 25 + .../img_similar_images_cleaner/process.py | 238 + .../sql/sql_config.json | 6 + .../filter/remove_duplicate_file/__init__.py | 6 + .../filter/remove_duplicate_file/metadata.yml | 25 + .../filter/remove_duplicate_file/process.py | 158 + .../remove_duplicate_file/sql/sql_config.json | 6 + .../__init__.py | 6 + .../metadata.yml | 25 + .../process.py | 116 + .../resources/political.txt | 321 + .../resources/sexual.txt | 288 + .../resources/special_symbols.txt | 50 + .../resources/violent.txt | 452 + .../__init__.py | 6 + .../metadata.yml | 34 + .../process.py | 54 + runtime/ops/formatter/__init__.py | 25 + .../ops/formatter/file_exporter/__init__.py | 6 + .../ops/formatter/file_exporter/metadata.yml | 16 + .../ops/formatter/file_exporter/process.py | 144 + .../ops/formatter/img_formatter/__init__.py | 6 + .../ops/formatter/img_formatter/metadata.yml | 16 + .../ops/formatter/img_formatter/process.py | 35 + .../ops/formatter/slide_formatter/__init__.py | 6 + .../formatter/slide_formatter/metadata.yml | 16 + .../ops/formatter/slide_formatter/process.py | 36 + .../ops/formatter/text_formatter/__init__.py | 6 + .../ops/formatter/text_formatter/metadata.yml | 16 + .../ops/formatter/text_formatter/process.py | 44 + .../ops/formatter/word_formatter/__init__.py | 6 + .../ops/formatter/word_formatter/metadata.yml | 16 + .../ops/formatter/word_formatter/process.py | 68 + runtime/ops/llms/__init__.py | 25 + .../llms/qa_condition_evaluator/__init__.py | 10 + .../llms/qa_condition_evaluator/metadata.yml | 16 + .../llms/qa_condition_evaluator/process.py | 98 + .../resources/examples.json | 36 + .../resources/readme.md | 107 + .../resources/template.txt | 17 + .../llms/text_quality_evaluation/__init__.py | 6 + .../llms/text_quality_evaluation/constant.py | 43 + .../llms/text_quality_evaluation/metadata.yml | 16 + .../llms/text_quality_evaluation/process.py | 113 + .../text_quality_evaluation/prompt_config.py | 32 + .../resources/examples.json | 98 + .../resources/template.txt | 17 + runtime/ops/mapper/__init__.py | 52 + .../ops/mapper/content_cleaner/__init__.py | 6 + .../ops/mapper/content_cleaner/metadata.yml | 16 + runtime/ops/mapper/content_cleaner/process.py | 64 + .../credit_card_number_cleaner/__init__.py | 6 + .../credit_card_number_cleaner/metadata.yml | 16 + .../credit_card_number_cleaner/process.py | 83 + runtime/ops/mapper/email_cleaner/__init__.py | 6 + runtime/ops/mapper/email_cleaner/metadata.yml | 16 + runtime/ops/mapper/email_cleaner/process.py | 47 + runtime/ops/mapper/emoji_cleaner/__init__.py | 6 + runtime/ops/mapper/emoji_cleaner/metadata.yml | 16 + runtime/ops/mapper/emoji_cleaner/process.py | 27 + .../mapper/extra_space_cleaner/__init__.py | 6 + .../mapper/extra_space_cleaner/metadata.yml | 17 + .../ops/mapper/extra_space_cleaner/process.py | 69 + .../resources/special_token.txt | 53 + .../full_width_characters_cleaner/__init__.py | 6 + .../metadata.yml | 18 + .../full_width_characters_cleaner/process.py | 46 + .../garble_characters_cleaner/__init__.py | 6 + .../garble_characters_cleaner/metadata.yml | 17 + .../garble_characters_cleaner/process.py | 54 + .../resources/charset.json | 24 + .../ops/mapper/html_tag_cleaner/__init__.py | 6 + .../ops/mapper/html_tag_cleaner/metadata.yml | 16 + .../ops/mapper/html_tag_cleaner/process.py | 80 + .../ops/mapper/id_number_cleaner/__init__.py | 6 + .../ops/mapper/id_number_cleaner/metadata.yml | 16 + .../ops/mapper/id_number_cleaner/process.py | 116 + .../resources/area_code_enum.txt | 3264 +++ runtime/ops/mapper/img_denoise/__init__.py | 6 + runtime/ops/mapper/img_denoise/metadata.yml | 17 + runtime/ops/mapper/img_denoise/process.py | 60 + .../mapper/img_direction_correct/__init__.py | 6 + .../img_direction_correct/base_model.py | 38 + .../mapper/img_direction_correct/metadata.yml | 17 + .../mapper/img_direction_correct/process.py | 139 + .../img_enhanced_brightness/__init__.py | 6 + .../img_enhanced_brightness/metadata.yml | 16 + .../mapper/img_enhanced_brightness/process.py | 100 + .../mapper/img_enhanced_contrast/__init__.py | 6 + .../mapper/img_enhanced_contrast/metadata.yml | 16 + .../mapper/img_enhanced_contrast/process.py | 71 + .../img_enhanced_saturation/__init__.py | 6 + .../img_enhanced_saturation/metadata.yml | 17 + .../mapper/img_enhanced_saturation/process.py | 81 + .../mapper/img_enhanced_sharpness/__init__.py | 6 + .../img_enhanced_sharpness/metadata.yml | 17 + .../mapper/img_enhanced_sharpness/process.py | 69 + .../__init__.py | 6 + .../metadata.yml | 17 + .../img_perspective_transformation/process.py | 147 + runtime/ops/mapper/img_resize/__init__.py | 6 + runtime/ops/mapper/img_resize/metadata.yml | 35 + runtime/ops/mapper/img_resize/process.py | 40 + .../ops/mapper/img_shadow_remove/__init__.py | 6 + .../ops/mapper/img_shadow_remove/metadata.yml | 17 + .../ops/mapper/img_shadow_remove/process.py | 72 + runtime/ops/mapper/img_type_unify/__init__.py | 6 + .../ops/mapper/img_type_unify/metadata.yml | 30 + runtime/ops/mapper/img_type_unify/process.py | 41 + .../mapper/img_watermark_remove/__init__.py | 6 + .../mapper/img_watermark_remove/metadata.yml | 26 + .../mapper/img_watermark_remove/process.py | 160 + .../watermark_ocr_model.py | 25 + .../invisible_characters_cleaner/__init__.py | 7 + .../invisible_characters_cleaner/metadata.yml | 16 + .../invisible_characters_cleaner/process.py | 30 + .../ops/mapper/ip_address_cleaner/__init__.py | 6 + .../mapper/ip_address_cleaner/metadata.yml | 16 + .../ops/mapper/ip_address_cleaner/process.py | 74 + .../knowledge_relation_slice/__init__.py | 6 + .../graph_sim_func.py | 108 + .../knowledge_relation.py | 184 + .../knowledge_slice.py | 23 + .../knowledge_relation_slice/metadata.yml | 16 + .../knowledge_relation_slice/process.py | 46 + runtime/ops/mapper/legend_cleaner/__init__.py | 6 + .../ops/mapper/legend_cleaner/metadata.yml | 16 + runtime/ops/mapper/legend_cleaner/process.py | 41 + .../mapper/phone_number_cleaner/__init__.py | 6 + .../mapper/phone_number_cleaner/metadata.yml | 16 + .../mapper/phone_number_cleaner/process.py | 51 + .../mapper/political_word_cleaner/__init__.py | 6 + .../political_word_cleaner/metadata.yml | 16 + .../mapper/political_word_cleaner/process.py | 67 + .../resources/political.txt | 321 + .../resources/special_symbols.txt | 50 + .../remove_duplicate_sentences/__init__.py | 6 + .../remove_duplicate_sentences/metadata.yml | 16 + .../remove_duplicate_sentences/process.py | 68 + .../__init__.py | 6 + .../metadata.yml | 16 + .../process.py | 70 + .../resources/sexual.txt | 288 + .../resources/special_symbols.txt | 50 + .../resources/violent.txt | 452 + runtime/ops/mapper/text_to_word/__init__.py | 6 + runtime/ops/mapper/text_to_word/metadata.yml | 16 + runtime/ops/mapper/text_to_word/process.py | 328 + .../mapper/traditional_chinese/__init__.py | 6 + .../mapper/traditional_chinese/metadata.yml | 16 + .../ops/mapper/traditional_chinese/process.py | 33 + .../mapper/unicode_space_cleaner/__init__.py | 6 + .../mapper/unicode_space_cleaner/metadata.yml | 16 + .../mapper/unicode_space_cleaner/process.py | 29 + runtime/ops/mapper/url_cleaner/__init__.py | 6 + runtime/ops/mapper/url_cleaner/metadata.yml | 16 + runtime/ops/mapper/url_cleaner/process.py | 36 + .../ops/mapper/xml_tag_cleaner/__init__.py | 6 + .../ops/mapper/xml_tag_cleaner/metadata.yml | 16 + runtime/ops/mapper/xml_tag_cleaner/process.py | 68 + runtime/ops/requirements.txt | 22 + runtime/ops/slicer/__init__.py | 22 + runtime/ops/slicer/segmentation/__init__.py | 6 + runtime/ops/slicer/segmentation/metadata.yml | 16 + runtime/ops/slicer/segmentation/process.py | 62 + .../slide_annotation_slicer/__init__.py | 6 + .../slide_annotation_slicer/metadata.yml | 16 + .../slicer/slide_annotation_slicer/process.py | 117 + .../slicer/slide_simple_slicer/__init__.py | 6 + .../slicer/slide_simple_slicer/metadata.yml | 43 + .../ops/slicer/slide_simple_slicer/process.py | 98 + runtime/ops/user/__init__.py | 27 + runtime/python-executor/README.md | 89 + runtime/python-executor/datamate/__init__.py | 1 + .../datamate/common/__init__.py | 0 .../datamate/common/error_code.py | 98 + .../datamate/common/utils/__init__.py | 59 + .../datamate/common/utils/aho_corasick.py | 115 + .../datamate/common/utils/bytes_transform.py | 66 + .../datamate/common/utils/custom_importer.py | 30 + .../datamate/common/utils/lazy_loader.py | 229 + .../datamate/common/utils/llm_request.py | 118 + .../common/utils/load_offline_module.py | 109 + .../datamate/common/utils/registry.py | 102 + .../datamate/common/utils/text_splitter.py | 171 + .../python-executor/datamate/core/__init__.py | 0 .../python-executor/datamate/core/base_op.py | 381 + .../python-executor/datamate/core/constant.py | 8 + .../python-executor/datamate/core/dataset.py | 213 + .../datamate/operator_runtime.py | 163 + .../python-executor/datamate/ops/__init__.py | 22 + .../datamate/scheduler/__init__.py | 6 + .../datamate/scheduler/cmd_task_scheduler.py | 214 + .../datamate/scheduler/func_task_scheduler.py | 133 + .../datamate/scheduler/scheduler.py | 160 + .../datamate/sql_manager/__init__.py | 2 + .../sql_manager/persistence_atction.py | 176 + .../datamate/sql_manager/sql/sql_config.json | 17 + .../datamate/sql_manager/sql_manager.py | 52 + .../datamate/wrappers/__init__.py | 6 + .../datamate/wrappers/data_juicer_wrapper.py | 6 + .../datamate/wrappers/datamate_executor.py | 131 + .../datamate/wrappers/datamate_wrapper.py | 15 + runtime/python-executor/pyproject.toml | 76 + scripts/db/00-database-init.sql | 1 + scripts/db/data-cleaning-init.sql | 103 + scripts/db/data-collection-init.sql | 160 + scripts/db/data-common-init.sql | 15 + scripts/db/data-management-init.sql | 156 + scripts/db/data-operator-init.sql | 223 + scripts/images/backend/Dockerfile | 45 + scripts/images/backend/settings.xml | 68 + scripts/images/backend/start.sh | 8 + scripts/images/datax/Dockerfile | 33 + scripts/images/datax/app.py | 52 + scripts/images/frontend/Dockerfile | 17 + scripts/images/frontend/edm.conf | 16 + scripts/images/mineru/Dockerfile | 22 + scripts/images/runtime/Dockerfile | 24 + scripts/images/unstructured/Dockerfile | 9 + scripts/images/unstructured/app.py | 61 + scripts/save_images.sh | 103 + 692 files changed, 135442 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/docker-image-backend.yml create mode 100644 .github/workflows/docker-image-frontend.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README-zh.md create mode 100644 README.md create mode 100644 backend/api-gateway/pom.xml create mode 100644 backend/api-gateway/src/main/java/com/datamate/gateway/ApiGatewayApplication.java create mode 100644 backend/openapi/README.md create mode 100644 backend/openapi/specs/data-annotation.yaml create mode 100644 backend/openapi/specs/data-cleaning.yaml create mode 100644 backend/openapi/specs/data-collection.yaml create mode 100644 backend/openapi/specs/data-evaluation.yaml create mode 100644 backend/openapi/specs/data-management.yaml create mode 100644 backend/openapi/specs/data-synthesis.yaml create mode 100644 backend/openapi/specs/execution-engine.yaml create mode 100644 backend/openapi/specs/operator-market.yaml create mode 100644 backend/openapi/specs/pipeline-orchestration.yaml create mode 100644 backend/pom.xml create mode 100644 backend/services/data-annotation-service/pom.xml create mode 100644 backend/services/data-cleaning-service/img.png create mode 100644 backend/services/data-cleaning-service/img1.png create mode 100644 backend/services/data-cleaning-service/img2.png create mode 100644 backend/services/data-cleaning-service/pom.xml create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/DataCleaningServiceConfiguration.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/DatasetClient.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/RuntimeClient.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/scheduler/CleaningTaskScheduler.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTaskService.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTemplateService.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/converter/OperatorInstanceConverter.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/CreateDatasetRequest.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetFileResponse.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetResponse.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetTypeResponse.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/ExecutorType.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/OperatorInstancePo.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/PagedDatasetFileResponse.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TaskProcess.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TemplateWithInstance.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/exception/CleanErrorCode.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningResultMapper.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTaskMapper.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTemplateMapper.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/OperatorInstanceMapper.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTaskController.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTemplateController.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningProcess.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTask.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTemplate.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTaskRequest.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTemplateRequest.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorInstance.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorResponse.java create mode 100644 backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/UpdateCleaningTemplateRequest.java create mode 100644 backend/services/data-cleaning-service/src/main/resources/mappers/CleaningResultMapper.xml create mode 100644 backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTaskMapper.xml create mode 100644 backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTemplateMapper.xml create mode 100644 backend/services/data-cleaning-service/src/main/resources/mappers/OperatorInstanceMapper.xml create mode 100644 backend/services/data-collection-service/README.md create mode 100644 backend/services/data-collection-service/image.png create mode 100644 backend/services/data-collection-service/image1.png create mode 100644 backend/services/data-collection-service/image2.png create mode 100644 backend/services/data-collection-service/image3.png create mode 100644 backend/services/data-collection-service/pom.xml create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/DataCollectionServiceConfiguration.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/application/scheduler/TaskSchedulerInitializer.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/CollectionTaskService.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/DataxExecutionService.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/TaskExecutionService.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/CollectionTask.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/DataxTemplate.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskExecution.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskStatus.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/CollectionTaskMapper.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/TaskExecutionMapper.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/typehandler/TaskStatusTypeHandler.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxJobBuilder.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProcessRunner.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProperties.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/converter/CollectionTaskConverter.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/CollectionTaskController.java create mode 100644 backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/TaskExecutionController.java create mode 100644 backend/services/data-collection-service/src/main/resources/config/application-datacollection.yml create mode 100644 backend/services/data-collection-service/src/main/resources/mappers/CollectionTaskMapper.xml create mode 100644 backend/services/data-collection-service/src/main/resources/mappers/TaskExecutionMapper.xml create mode 100644 backend/services/data-evaluation-service/pom.xml create mode 100644 backend/services/data-management-service/pom.xml create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/DataManagementServiceConfiguration.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/FileMetadataService.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/TagApplicationService.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetStatusType.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetType.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/contants/DatasetConstant.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Dataset.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFile.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFileUploadCheckInfo.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/StatusConstants.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Tag.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/CollectionTaskClient.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/CollectionTaskDetailResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/LocalCollectionConfig.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementConfig.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/exception/DataManagementErrorCode.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetFileMapper.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetMapper.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/TagMapper.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetFileRepository.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetRepository.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetFileRepositoryImpl.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetRepositoryImpl.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/DatasetConverter.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/TagConverter.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/AllDatasetStatisticsResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateDatasetRequest.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateTagRequest.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetFileResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetPagingQuery.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetStatisticsResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetTypeResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetFileResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/TagResponse.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateDatasetRequest.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateTagRequest.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFileRequest.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFilesPreRequest.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetController.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetFileController.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetTypeController.java create mode 100644 backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/TagController.java create mode 100644 backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml create mode 100644 backend/services/data-management-service/src/main/resources/mappers/DatasetFileMapper.xml create mode 100644 backend/services/data-management-service/src/main/resources/mappers/DatasetMapper.xml create mode 100644 backend/services/data-management-service/src/main/resources/mappers/TagMapper.xml create mode 100644 backend/services/data-synthesis-service/pom.xml create mode 100644 backend/services/execution-engine-service/pom.xml create mode 100644 backend/services/main-application/pom.xml create mode 100644 backend/services/main-application/src/main/java/com/datamate/main/DataMatePlatformApplication.java create mode 100644 backend/services/main-application/src/main/java/com/datamate/main/config/SecurityConfig.java create mode 100644 backend/services/main-application/src/main/resources/application.yml create mode 100644 backend/services/main-application/src/main/resources/config/application-datacollection.yml create mode 100644 backend/services/main-application/src/main/resources/config/application-datamanagement.yml create mode 100644 backend/services/main-application/src/main/resources/log4j2.xml create mode 100644 backend/services/operator-market-service/img.png create mode 100644 backend/services/operator-market-service/img_1.png create mode 100644 backend/services/operator-market-service/pom.xml create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/OperatorMarketServiceConfiguration.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/application/LabelService.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/application/OperatorService.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/converter/OperatorConverter.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/modal/Category.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/modal/CategoryRelation.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/modal/Operator.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/domain/modal/RelationCategoryDTO.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/CategoryMapper.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/CategoryRelationMapper.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/infrastructure/persistence/mapper/OperatorMapper.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/api/CategoryController.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/api/LabelController.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/api/OperatorController.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/CategoryTreeResponse.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/CreateOperatorRequest.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/Label.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorResponse.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/OperatorsListPostRequest.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/SubCategory.java create mode 100644 backend/services/operator-market-service/src/main/java/com/datamate/operator/interfaces/dto/UpdateOperatorRequest.java create mode 100644 backend/services/operator-market-service/src/main/resources/mappers/CategoryMapper.xml create mode 100644 backend/services/operator-market-service/src/main/resources/mappers/CategoryRelationMapper.xml create mode 100644 backend/services/operator-market-service/src/main/resources/mappers/OperatorMapper.xml create mode 100644 backend/services/pipeline-orchestration-service/pom.xml create mode 100644 backend/services/rag-indexer-service/pom.xml create mode 100644 backend/services/rag-indexer-service/src/main/java/com/dataengine/rag/indexer/RagApplication.java create mode 100644 backend/services/rag-query-service/pom.xml create mode 100644 backend/shared/domain-common/pom.xml create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/AggregateRoot.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/ValueObject.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/model/ChunkUploadPreRequest.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/model/ChunkUploadRequest.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/model/FileUploadResult.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/model/UploadCheckInfo.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/model/base/BaseEntity.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/service/FileService.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/utils/AnalyzerUtils.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/utils/ChunksSaver.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/domain/utils/CommonUtils.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/common/IgnoreResponseWrap.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/common/Response.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/EntityMetaObjectHandler.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalExceptionHandler.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalResponseHandler.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/MybatisPlusConfig.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessAssert.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessException.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCode.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCodeImpl.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/SystemErrorCode.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/mapper/ChunkUploadRequestMapper.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagedResponse.java create mode 100644 backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagingQuery.java create mode 100644 backend/shared/domain-common/src/main/resources/mappers/ChunkUploadRequestMapper.xml create mode 100644 backend/shared/security-common/pom.xml create mode 100644 backend/shared/security-common/src/main/java/com/datamate/common/security/JwtUtils.java create mode 100644 deployment/docker/datamate/docker-compose.yml create mode 100644 deployment/docker/datamate/utf8.cnf create mode 100644 deployment/helm/ray/kuberay-operator/Chart.yaml create mode 100644 deployment/helm/ray/kuberay-operator/crds/ray.io_rayclusters.yaml create mode 100644 deployment/helm/ray/kuberay-operator/crds/ray.io_rayjobs.yaml create mode 100644 deployment/helm/ray/kuberay-operator/crds/ray.io_rayservices.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/_helpers.tpl create mode 100644 deployment/helm/ray/kuberay-operator/templates/deployment.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/leader_election_role.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/leader_election_role_binding.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_role.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_rolebinding.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/ray_rayjob_editor_role.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/ray_rayjob_viewer_role.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/ray_rayservice_editor_role.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/ray_rayservice_viewer_role.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/role.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/rolebinding.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/service.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/serviceaccount.yaml create mode 100644 deployment/helm/ray/kuberay-operator/templates/servicemonitor.yaml create mode 100644 deployment/helm/ray/kuberay-operator/values.yaml create mode 100644 deployment/helm/ray/ray-cluster/Chart.yaml create mode 100644 deployment/helm/ray/ray-cluster/templates/_helpers.tpl create mode 100644 deployment/helm/ray/ray-cluster/templates/raycluster-cluster.yaml create mode 100644 deployment/helm/ray/ray-cluster/values.yaml create mode 100644 deployment/helm/ray/service.yaml create mode 100644 deployment/kubernetes/backend/deploy.yaml create mode 100644 deployment/kubernetes/datax/deploy.yaml create mode 100644 deployment/kubernetes/frontend/deploy.yaml create mode 100644 deployment/kubernetes/mineru/deploy.yaml create mode 100644 deployment/kubernetes/mysql/configmap.yaml create mode 100644 deployment/kubernetes/mysql/deploy.yaml create mode 100644 deployment/kubernetes/unstructured/deploy.yaml create mode 100644 editions/community/config/application.yml create mode 100644 editions/community/config/log4j2.xml create mode 100644 editions/enterprise/config/application.yml create mode 100644 editions/enterprise/config/log4j2.xml create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/huawei-logo.webp create mode 100644 frontend/src/components/AddTagPopover.tsx create mode 100644 frontend/src/components/CardView.tsx create mode 100644 frontend/src/components/DetailHeader.tsx create mode 100644 frontend/src/components/DevelopmentInProgress.tsx create mode 100644 frontend/src/components/ErrorBoundary.tsx create mode 100644 frontend/src/components/RadioCard.tsx create mode 100644 frontend/src/components/SearchControls.tsx create mode 100644 frontend/src/components/TagList.tsx create mode 100644 frontend/src/components/TagManagement.tsx create mode 100644 frontend/src/components/TaskPopover.tsx create mode 100644 frontend/src/components/TopLoadingBar.tsx create mode 100644 frontend/src/hooks/useDebouncedEffect.ts create mode 100644 frontend/src/hooks/useFetchData.ts create mode 100644 frontend/src/hooks/useLeavePrompt.ts create mode 100644 frontend/src/hooks/useSearchParams.tsx create mode 100644 frontend/src/hooks/useStyle.ts create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/mock/annotation.tsx create mode 100644 frontend/src/mock/cleansing.tsx create mode 100644 frontend/src/mock/evaluation.tsx create mode 100644 frontend/src/mock/knowledgeBase.tsx create mode 100644 frontend/src/mock/mock-apis.cjs create mode 100644 frontend/src/mock/mock-core/module-loader.cjs create mode 100644 frontend/src/mock/mock-core/session-helper.cjs create mode 100644 frontend/src/mock/mock-core/util.cjs create mode 100644 frontend/src/mock/mock-middleware/error-handle-middleware.cjs create mode 100644 frontend/src/mock/mock-middleware/index.cjs create mode 100644 frontend/src/mock/mock-middleware/send-json-middleawre.cjs create mode 100644 frontend/src/mock/mock-middleware/set-header-middleware.cjs create mode 100644 frontend/src/mock/mock-middleware/strong-match-middleware.cjs create mode 100644 frontend/src/mock/mock-seed/data-annotation.cjs create mode 100644 frontend/src/mock/mock-seed/data-cleansing.cjs create mode 100644 frontend/src/mock/mock-seed/data-collection.cjs create mode 100644 frontend/src/mock/mock-seed/data-evaluation.cjs create mode 100644 frontend/src/mock/mock-seed/data-management.cjs create mode 100644 frontend/src/mock/mock-seed/data-synthesis.cjs create mode 100644 frontend/src/mock/mock-seed/knowledge-generation.cjs create mode 100644 frontend/src/mock/mock-seed/operator-market.cjs create mode 100644 frontend/src/mock/mock.cjs create mode 100644 frontend/src/mock/nodemon.json create mode 100644 frontend/src/mock/operator.tsx create mode 100644 frontend/src/mock/ratio.tsx create mode 100644 frontend/src/mock/synthesis.tsx create mode 100644 frontend/src/pages/Agent/Agent.tsx create mode 100644 frontend/src/pages/DataAnnotation/Annotate/AnnotationWorkSpace.tsx create mode 100644 frontend/src/pages/DataAnnotation/Annotate/components/AudioAnnotation.tsx create mode 100644 frontend/src/pages/DataAnnotation/Annotate/components/ImageAnnotation.tsx create mode 100644 frontend/src/pages/DataAnnotation/Annotate/components/TextAnnotation.tsx create mode 100644 frontend/src/pages/DataAnnotation/Annotate/components/VideoAnnotation.tsx create mode 100644 frontend/src/pages/DataAnnotation/Create/CreateTask.tsx create mode 100644 frontend/src/pages/DataAnnotation/Create/components/CreateAnnptationTaskDialog.tsx create mode 100644 frontend/src/pages/DataAnnotation/Create/components/CustomTemplateDialog.tsx create mode 100644 frontend/src/pages/DataAnnotation/Detail/TaskDetail.tsx create mode 100644 frontend/src/pages/DataAnnotation/Home/DataAnnotation.tsx create mode 100644 frontend/src/pages/DataAnnotation/annotation.api.ts create mode 100644 frontend/src/pages/DataAnnotation/annotation.const.tsx create mode 100644 frontend/src/pages/DataAnnotation/annotation.model.ts create mode 100644 frontend/src/pages/DataCleansing/Create/CreateTask.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/CreateTempate.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/DragDrop.css create mode 100644 frontend/src/pages/DataCleansing/Create/DragExample.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/components/CreateTaskStepOne.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/components/CreateTemplateStepOne.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/components/OperatorConfig.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/components/OperatorLibrary.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/components/OperatorOrchestration.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/components/ParamConfig.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/hooks/useCreateStepTwo.tsx create mode 100644 frontend/src/pages/DataCleansing/Create/hooks/useDragOperators.ts create mode 100644 frontend/src/pages/DataCleansing/Create/hooks/useOperatorOperations.ts create mode 100644 frontend/src/pages/DataCleansing/Detail/TaskDetail.tsx create mode 100644 frontend/src/pages/DataCleansing/Detail/components/BasicInfo.tsx create mode 100644 frontend/src/pages/DataCleansing/Detail/components/FileTable.tsx create mode 100644 frontend/src/pages/DataCleansing/Detail/components/LogsTable.tsx create mode 100644 frontend/src/pages/DataCleansing/Detail/components/OperatorTable.tsx create mode 100644 frontend/src/pages/DataCleansing/Home/DataCleansing.tsx create mode 100644 frontend/src/pages/DataCleansing/Home/components/ProcessFlowDiagram.tsx create mode 100644 frontend/src/pages/DataCleansing/Home/components/TaskList.tsx create mode 100644 frontend/src/pages/DataCleansing/Home/components/TemplateList.tsx create mode 100644 frontend/src/pages/DataCleansing/cleansing.api.ts create mode 100644 frontend/src/pages/DataCleansing/cleansing.const.tsx create mode 100644 frontend/src/pages/DataCleansing/cleansing.model.ts create mode 100644 frontend/src/pages/DataCollection/Create/CreateTask.tsx create mode 100644 frontend/src/pages/DataCollection/Home/DataCollection.tsx create mode 100644 frontend/src/pages/DataCollection/Home/components/ExecutionLog.tsx create mode 100644 frontend/src/pages/DataCollection/Home/components/TaskManagement.tsx create mode 100644 frontend/src/pages/DataCollection/collection.apis.ts create mode 100644 frontend/src/pages/DataCollection/collection.const.ts create mode 100644 frontend/src/pages/DataCollection/collection.model.ts create mode 100644 frontend/src/pages/DataEvaluation/Create/CreateTask.tsx create mode 100644 frontend/src/pages/DataEvaluation/Evaluate/ManualEvaluate.tsx create mode 100644 frontend/src/pages/DataEvaluation/Home/DataEvaluation.tsx create mode 100644 frontend/src/pages/DataEvaluation/Report/EvaluationReport.tsx create mode 100644 frontend/src/pages/DataEvaluation/data-evaluation.api.ts create mode 100644 frontend/src/pages/DataEvaluation/data-evaluation.d.ts create mode 100644 frontend/src/pages/DataManagement/Create/CreateDataset.tsx create mode 100644 frontend/src/pages/DataManagement/Create/EditDataset.tsx create mode 100644 frontend/src/pages/DataManagement/Create/components/BasicInformation.tsx create mode 100644 frontend/src/pages/DataManagement/Detail/DatasetDetail.tsx create mode 100644 frontend/src/pages/DataManagement/Detail/components/DataLineageFlow.tsx create mode 100644 frontend/src/pages/DataManagement/Detail/components/DataQuality.tsx create mode 100644 frontend/src/pages/DataManagement/Detail/components/ImportConfiguration.tsx create mode 100644 frontend/src/pages/DataManagement/Detail/components/Overview.tsx create mode 100644 frontend/src/pages/DataManagement/Home/DataManagement.tsx create mode 100644 frontend/src/pages/DataManagement/dataset.api.ts create mode 100644 frontend/src/pages/DataManagement/dataset.const.tsx create mode 100644 frontend/src/pages/DataManagement/dataset.model.ts create mode 100644 frontend/src/pages/DataManagement/hooks/index.ts create mode 100644 frontend/src/pages/DataManagement/hooks/useFilesOperation.ts create mode 100644 frontend/src/pages/DataManagement/hooks/useImportFile.tsx create mode 100644 frontend/src/pages/Home/Home.tsx create mode 100644 frontend/src/pages/KnowledgeGeneration/Create/KnowledgeBaseCreate.tsx create mode 100644 frontend/src/pages/KnowledgeGeneration/Detail/KnowledgeBaseDetail.tsx create mode 100644 frontend/src/pages/KnowledgeGeneration/FileDetail/KnowledgeBaseFileDetail.tsx create mode 100644 frontend/src/pages/KnowledgeGeneration/Home/KnowledgeGeneration.tsx create mode 100644 frontend/src/pages/KnowledgeGeneration/knowledge-base.api.ts create mode 100644 frontend/src/pages/KnowledgeGeneration/knowledge-base.const.ts create mode 100644 frontend/src/pages/KnowledgeGeneration/knowledge-base.model.ts create mode 100644 frontend/src/pages/Layout/MainLayout.tsx create mode 100644 frontend/src/pages/Layout/Sidebar.tsx create mode 100644 frontend/src/pages/Layout/TaskUpload.tsx create mode 100644 frontend/src/pages/Layout/menu.tsx create mode 100644 frontend/src/pages/OperatorMarket/Create/OperatorPluginCreate.tsx create mode 100644 frontend/src/pages/OperatorMarket/Create/components/ConfigureStep.tsx create mode 100644 frontend/src/pages/OperatorMarket/Create/components/ParsingStep.tsx create mode 100644 frontend/src/pages/OperatorMarket/Create/components/PreviewStep.tsx create mode 100644 frontend/src/pages/OperatorMarket/Create/components/UploadStep.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/OperatorPluginDetail.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/ChangeLog.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/Documentation.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/Examples.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/Install.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/Overview.tsx create mode 100644 frontend/src/pages/OperatorMarket/Detail/components/Reviews.tsx create mode 100644 frontend/src/pages/OperatorMarket/Home/OperatorMarket.tsx create mode 100644 frontend/src/pages/OperatorMarket/Home/components/Filters.tsx create mode 100644 frontend/src/pages/OperatorMarket/Home/components/List.tsx create mode 100644 frontend/src/pages/OperatorMarket/OperatorPluginEdit.tsx create mode 100644 frontend/src/pages/OperatorMarket/operator.api.ts create mode 100644 frontend/src/pages/OperatorMarket/operator.const.tsx create mode 100644 frontend/src/pages/OperatorMarket/operator.model.ts create mode 100644 frontend/src/pages/Orchestration/Orchestration.tsx create mode 100644 frontend/src/pages/Orchestration/WorkflowEditor.tsx create mode 100644 frontend/src/pages/Orchestration/components/CustomNode.tsx create mode 100644 frontend/src/pages/RatioTask/CreateRatioTask.tsx create mode 100644 frontend/src/pages/RatioTask/RatioTask.tsx create mode 100644 frontend/src/pages/RatioTask/ratio.d.ts create mode 100644 frontend/src/pages/SettingsPage/Settings.tsx create mode 100644 frontend/src/pages/SettingsPage/components/EnvironmentAccess.tsx create mode 100644 frontend/src/pages/SettingsPage/components/SystemConfig.tsx create mode 100644 frontend/src/pages/SettingsPage/components/WebhookConfig.tsx create mode 100644 frontend/src/pages/SynthesisTask/CreateTask.tsx create mode 100644 frontend/src/pages/SynthesisTask/CreateTemplate.tsx create mode 100644 frontend/src/pages/SynthesisTask/DataSynthesis.tsx create mode 100644 frontend/src/pages/SynthesisTask/components/InstructionTemplateTab.tsx create mode 100644 frontend/src/pages/SynthesisTask/components/SynthesisTaskTab.tsx create mode 100644 frontend/src/pages/SynthesisTask/synthesis.d.ts create mode 100644 frontend/src/pages/TaskManagement/TaskManagement.tsx create mode 100644 frontend/src/pages/globals.css create mode 100644 frontend/src/providers/MultiProvider.js create mode 100644 frontend/src/routes/routes.ts create mode 100644 frontend/src/utils/file.util.ts create mode 100644 frontend/src/utils/loading.ts create mode 100644 frontend/src/utils/request.ts create mode 100644 frontend/src/utils/unit.ts create mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/tailwind.config.ts create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts create mode 100644 runtime/datax/nfsreader/pom.xml create mode 100644 runtime/datax/nfsreader/src/main/assembly/package.xml create mode 100644 runtime/datax/nfsreader/src/main/java/com/modelengine/edatamate/plugin/reader/nfsreader/MountUtil.java create mode 100644 runtime/datax/nfsreader/src/main/java/com/modelengine/edatamate/plugin/reader/nfsreader/NfsReader.java create mode 100644 runtime/datax/nfsreader/src/main/resources/plugin.json create mode 100644 runtime/datax/nfsreader/src/main/resources/plugin_job_template.json create mode 100644 runtime/datax/nfswriter/pom.xml create mode 100644 runtime/datax/nfswriter/src/main/assembly/package.xml create mode 100644 runtime/datax/nfswriter/src/main/java/com/modelengine/edatamate/plugin/writer/nfswriter/MountUtil.java create mode 100644 runtime/datax/nfswriter/src/main/java/com/modelengine/edatamate/plugin/writer/nfswriter/NfsWriter.java create mode 100644 runtime/datax/nfswriter/src/main/java/com/modelengine/edatamate/plugin/writer/nfswriter/ShellUtil.java create mode 100644 runtime/datax/nfswriter/src/main/resources/plugin.json create mode 100644 runtime/datax/nfswriter/src/main/resources/plugin_job_template.json create mode 100644 runtime/datax/package.xml create mode 100644 runtime/datax/pom.xml create mode 100644 runtime/ops/README.md create mode 100644 runtime/ops/examples/text_length_filter/metadata.json create mode 100644 runtime/ops/examples/text_length_filter/operator.py create mode 100644 runtime/ops/filter/__init__.py create mode 100644 runtime/ops/filter/file_with_high_repeat_phrase_rate_filter/__init__.py create mode 100644 runtime/ops/filter/file_with_high_repeat_phrase_rate_filter/metadata.yml create mode 100644 runtime/ops/filter/file_with_high_repeat_phrase_rate_filter/process.py create mode 100644 runtime/ops/filter/file_with_high_repeat_phrase_rate_filter/resources/hit_stopwords.txt create mode 100644 runtime/ops/filter/file_with_high_repeat_word_rate_filter/__init__.py create mode 100644 runtime/ops/filter/file_with_high_repeat_word_rate_filter/metadata.yml create mode 100644 runtime/ops/filter/file_with_high_repeat_word_rate_filter/process.py create mode 100644 runtime/ops/filter/file_with_high_special_char_rate_filter/__init__.py create mode 100644 runtime/ops/filter/file_with_high_special_char_rate_filter/metadata.yml create mode 100644 runtime/ops/filter/file_with_high_special_char_rate_filter/process.py create mode 100644 runtime/ops/filter/file_with_high_special_char_rate_filter/resources/special_token.txt create mode 100644 runtime/ops/filter/img_advertisement_images_cleaner/__init__.py create mode 100644 runtime/ops/filter/img_advertisement_images_cleaner/metadata.yml create mode 100644 runtime/ops/filter/img_advertisement_images_cleaner/process.py create mode 100644 runtime/ops/filter/img_advertisement_images_cleaner/wechat_qrcode_model.py create mode 100644 runtime/ops/filter/img_blurred_images_cleaner/__init__.py create mode 100644 runtime/ops/filter/img_blurred_images_cleaner/metadata.yml create mode 100644 runtime/ops/filter/img_blurred_images_cleaner/process.py create mode 100644 runtime/ops/filter/img_duplicated_images_cleaner/__init__.py create mode 100644 runtime/ops/filter/img_duplicated_images_cleaner/metadata.yml create mode 100644 runtime/ops/filter/img_duplicated_images_cleaner/process.py create mode 100644 runtime/ops/filter/img_duplicated_images_cleaner/sql/sql_config.json create mode 100644 runtime/ops/filter/img_similar_images_cleaner/__init__.py create mode 100644 runtime/ops/filter/img_similar_images_cleaner/metadata.yml create mode 100644 runtime/ops/filter/img_similar_images_cleaner/process.py create mode 100644 runtime/ops/filter/img_similar_images_cleaner/sql/sql_config.json create mode 100644 runtime/ops/filter/remove_duplicate_file/__init__.py create mode 100644 runtime/ops/filter/remove_duplicate_file/metadata.yml create mode 100644 runtime/ops/filter/remove_duplicate_file/process.py create mode 100644 runtime/ops/filter/remove_duplicate_file/sql/sql_config.json create mode 100644 runtime/ops/filter/remove_file_with_many_sensitive_words/__init__.py create mode 100644 runtime/ops/filter/remove_file_with_many_sensitive_words/metadata.yml create mode 100644 runtime/ops/filter/remove_file_with_many_sensitive_words/process.py create mode 100644 runtime/ops/filter/remove_file_with_many_sensitive_words/resources/political.txt create mode 100644 runtime/ops/filter/remove_file_with_many_sensitive_words/resources/sexual.txt create mode 100644 runtime/ops/filter/remove_file_with_many_sensitive_words/resources/special_symbols.txt create mode 100644 runtime/ops/filter/remove_file_with_many_sensitive_words/resources/violent.txt create mode 100644 runtime/ops/filter/remove_file_with_short_or_long_length/__init__.py create mode 100644 runtime/ops/filter/remove_file_with_short_or_long_length/metadata.yml create mode 100644 runtime/ops/filter/remove_file_with_short_or_long_length/process.py create mode 100644 runtime/ops/formatter/__init__.py create mode 100644 runtime/ops/formatter/file_exporter/__init__.py create mode 100644 runtime/ops/formatter/file_exporter/metadata.yml create mode 100644 runtime/ops/formatter/file_exporter/process.py create mode 100644 runtime/ops/formatter/img_formatter/__init__.py create mode 100644 runtime/ops/formatter/img_formatter/metadata.yml create mode 100644 runtime/ops/formatter/img_formatter/process.py create mode 100644 runtime/ops/formatter/slide_formatter/__init__.py create mode 100644 runtime/ops/formatter/slide_formatter/metadata.yml create mode 100644 runtime/ops/formatter/slide_formatter/process.py create mode 100644 runtime/ops/formatter/text_formatter/__init__.py create mode 100644 runtime/ops/formatter/text_formatter/metadata.yml create mode 100644 runtime/ops/formatter/text_formatter/process.py create mode 100644 runtime/ops/formatter/word_formatter/__init__.py create mode 100644 runtime/ops/formatter/word_formatter/metadata.yml create mode 100644 runtime/ops/formatter/word_formatter/process.py create mode 100644 runtime/ops/llms/__init__.py create mode 100644 runtime/ops/llms/qa_condition_evaluator/__init__.py create mode 100644 runtime/ops/llms/qa_condition_evaluator/metadata.yml create mode 100644 runtime/ops/llms/qa_condition_evaluator/process.py create mode 100644 runtime/ops/llms/qa_condition_evaluator/resources/examples.json create mode 100644 runtime/ops/llms/qa_condition_evaluator/resources/readme.md create mode 100644 runtime/ops/llms/qa_condition_evaluator/resources/template.txt create mode 100644 runtime/ops/llms/text_quality_evaluation/__init__.py create mode 100644 runtime/ops/llms/text_quality_evaluation/constant.py create mode 100644 runtime/ops/llms/text_quality_evaluation/metadata.yml create mode 100644 runtime/ops/llms/text_quality_evaluation/process.py create mode 100644 runtime/ops/llms/text_quality_evaluation/prompt_config.py create mode 100644 runtime/ops/llms/text_quality_evaluation/resources/examples.json create mode 100644 runtime/ops/llms/text_quality_evaluation/resources/template.txt create mode 100644 runtime/ops/mapper/__init__.py create mode 100644 runtime/ops/mapper/content_cleaner/__init__.py create mode 100644 runtime/ops/mapper/content_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/content_cleaner/process.py create mode 100644 runtime/ops/mapper/credit_card_number_cleaner/__init__.py create mode 100644 runtime/ops/mapper/credit_card_number_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/credit_card_number_cleaner/process.py create mode 100644 runtime/ops/mapper/email_cleaner/__init__.py create mode 100644 runtime/ops/mapper/email_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/email_cleaner/process.py create mode 100644 runtime/ops/mapper/emoji_cleaner/__init__.py create mode 100644 runtime/ops/mapper/emoji_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/emoji_cleaner/process.py create mode 100644 runtime/ops/mapper/extra_space_cleaner/__init__.py create mode 100644 runtime/ops/mapper/extra_space_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/extra_space_cleaner/process.py create mode 100644 runtime/ops/mapper/extra_space_cleaner/resources/special_token.txt create mode 100644 runtime/ops/mapper/full_width_characters_cleaner/__init__.py create mode 100644 runtime/ops/mapper/full_width_characters_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/full_width_characters_cleaner/process.py create mode 100644 runtime/ops/mapper/garble_characters_cleaner/__init__.py create mode 100644 runtime/ops/mapper/garble_characters_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/garble_characters_cleaner/process.py create mode 100644 runtime/ops/mapper/garble_characters_cleaner/resources/charset.json create mode 100644 runtime/ops/mapper/html_tag_cleaner/__init__.py create mode 100644 runtime/ops/mapper/html_tag_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/html_tag_cleaner/process.py create mode 100644 runtime/ops/mapper/id_number_cleaner/__init__.py create mode 100644 runtime/ops/mapper/id_number_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/id_number_cleaner/process.py create mode 100644 runtime/ops/mapper/id_number_cleaner/resources/area_code_enum.txt create mode 100644 runtime/ops/mapper/img_denoise/__init__.py create mode 100644 runtime/ops/mapper/img_denoise/metadata.yml create mode 100644 runtime/ops/mapper/img_denoise/process.py create mode 100644 runtime/ops/mapper/img_direction_correct/__init__.py create mode 100644 runtime/ops/mapper/img_direction_correct/base_model.py create mode 100644 runtime/ops/mapper/img_direction_correct/metadata.yml create mode 100644 runtime/ops/mapper/img_direction_correct/process.py create mode 100644 runtime/ops/mapper/img_enhanced_brightness/__init__.py create mode 100644 runtime/ops/mapper/img_enhanced_brightness/metadata.yml create mode 100644 runtime/ops/mapper/img_enhanced_brightness/process.py create mode 100644 runtime/ops/mapper/img_enhanced_contrast/__init__.py create mode 100644 runtime/ops/mapper/img_enhanced_contrast/metadata.yml create mode 100644 runtime/ops/mapper/img_enhanced_contrast/process.py create mode 100644 runtime/ops/mapper/img_enhanced_saturation/__init__.py create mode 100644 runtime/ops/mapper/img_enhanced_saturation/metadata.yml create mode 100644 runtime/ops/mapper/img_enhanced_saturation/process.py create mode 100644 runtime/ops/mapper/img_enhanced_sharpness/__init__.py create mode 100644 runtime/ops/mapper/img_enhanced_sharpness/metadata.yml create mode 100644 runtime/ops/mapper/img_enhanced_sharpness/process.py create mode 100644 runtime/ops/mapper/img_perspective_transformation/__init__.py create mode 100644 runtime/ops/mapper/img_perspective_transformation/metadata.yml create mode 100644 runtime/ops/mapper/img_perspective_transformation/process.py create mode 100644 runtime/ops/mapper/img_resize/__init__.py create mode 100644 runtime/ops/mapper/img_resize/metadata.yml create mode 100644 runtime/ops/mapper/img_resize/process.py create mode 100644 runtime/ops/mapper/img_shadow_remove/__init__.py create mode 100644 runtime/ops/mapper/img_shadow_remove/metadata.yml create mode 100644 runtime/ops/mapper/img_shadow_remove/process.py create mode 100644 runtime/ops/mapper/img_type_unify/__init__.py create mode 100644 runtime/ops/mapper/img_type_unify/metadata.yml create mode 100644 runtime/ops/mapper/img_type_unify/process.py create mode 100644 runtime/ops/mapper/img_watermark_remove/__init__.py create mode 100644 runtime/ops/mapper/img_watermark_remove/metadata.yml create mode 100644 runtime/ops/mapper/img_watermark_remove/process.py create mode 100644 runtime/ops/mapper/img_watermark_remove/watermark_ocr_model.py create mode 100644 runtime/ops/mapper/invisible_characters_cleaner/__init__.py create mode 100644 runtime/ops/mapper/invisible_characters_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/invisible_characters_cleaner/process.py create mode 100644 runtime/ops/mapper/ip_address_cleaner/__init__.py create mode 100644 runtime/ops/mapper/ip_address_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/ip_address_cleaner/process.py create mode 100644 runtime/ops/mapper/knowledge_relation_slice/__init__.py create mode 100644 runtime/ops/mapper/knowledge_relation_slice/graph_sim_func.py create mode 100644 runtime/ops/mapper/knowledge_relation_slice/knowledge_relation.py create mode 100644 runtime/ops/mapper/knowledge_relation_slice/knowledge_slice.py create mode 100644 runtime/ops/mapper/knowledge_relation_slice/metadata.yml create mode 100644 runtime/ops/mapper/knowledge_relation_slice/process.py create mode 100644 runtime/ops/mapper/legend_cleaner/__init__.py create mode 100644 runtime/ops/mapper/legend_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/legend_cleaner/process.py create mode 100644 runtime/ops/mapper/phone_number_cleaner/__init__.py create mode 100644 runtime/ops/mapper/phone_number_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/phone_number_cleaner/process.py create mode 100644 runtime/ops/mapper/political_word_cleaner/__init__.py create mode 100644 runtime/ops/mapper/political_word_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/political_word_cleaner/process.py create mode 100644 runtime/ops/mapper/political_word_cleaner/resources/political.txt create mode 100644 runtime/ops/mapper/political_word_cleaner/resources/special_symbols.txt create mode 100644 runtime/ops/mapper/remove_duplicate_sentences/__init__.py create mode 100644 runtime/ops/mapper/remove_duplicate_sentences/metadata.yml create mode 100644 runtime/ops/mapper/remove_duplicate_sentences/process.py create mode 100644 runtime/ops/mapper/sexual_and_violent_word_cleaner/__init__.py create mode 100644 runtime/ops/mapper/sexual_and_violent_word_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/sexual_and_violent_word_cleaner/process.py create mode 100644 runtime/ops/mapper/sexual_and_violent_word_cleaner/resources/sexual.txt create mode 100644 runtime/ops/mapper/sexual_and_violent_word_cleaner/resources/special_symbols.txt create mode 100644 runtime/ops/mapper/sexual_and_violent_word_cleaner/resources/violent.txt create mode 100644 runtime/ops/mapper/text_to_word/__init__.py create mode 100644 runtime/ops/mapper/text_to_word/metadata.yml create mode 100644 runtime/ops/mapper/text_to_word/process.py create mode 100644 runtime/ops/mapper/traditional_chinese/__init__.py create mode 100644 runtime/ops/mapper/traditional_chinese/metadata.yml create mode 100644 runtime/ops/mapper/traditional_chinese/process.py create mode 100644 runtime/ops/mapper/unicode_space_cleaner/__init__.py create mode 100644 runtime/ops/mapper/unicode_space_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/unicode_space_cleaner/process.py create mode 100644 runtime/ops/mapper/url_cleaner/__init__.py create mode 100644 runtime/ops/mapper/url_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/url_cleaner/process.py create mode 100644 runtime/ops/mapper/xml_tag_cleaner/__init__.py create mode 100644 runtime/ops/mapper/xml_tag_cleaner/metadata.yml create mode 100644 runtime/ops/mapper/xml_tag_cleaner/process.py create mode 100644 runtime/ops/requirements.txt create mode 100644 runtime/ops/slicer/__init__.py create mode 100644 runtime/ops/slicer/segmentation/__init__.py create mode 100644 runtime/ops/slicer/segmentation/metadata.yml create mode 100644 runtime/ops/slicer/segmentation/process.py create mode 100644 runtime/ops/slicer/slide_annotation_slicer/__init__.py create mode 100644 runtime/ops/slicer/slide_annotation_slicer/metadata.yml create mode 100644 runtime/ops/slicer/slide_annotation_slicer/process.py create mode 100644 runtime/ops/slicer/slide_simple_slicer/__init__.py create mode 100644 runtime/ops/slicer/slide_simple_slicer/metadata.yml create mode 100644 runtime/ops/slicer/slide_simple_slicer/process.py create mode 100644 runtime/ops/user/__init__.py create mode 100644 runtime/python-executor/README.md create mode 100644 runtime/python-executor/datamate/__init__.py create mode 100644 runtime/python-executor/datamate/common/__init__.py create mode 100644 runtime/python-executor/datamate/common/error_code.py create mode 100644 runtime/python-executor/datamate/common/utils/__init__.py create mode 100644 runtime/python-executor/datamate/common/utils/aho_corasick.py create mode 100644 runtime/python-executor/datamate/common/utils/bytes_transform.py create mode 100644 runtime/python-executor/datamate/common/utils/custom_importer.py create mode 100644 runtime/python-executor/datamate/common/utils/lazy_loader.py create mode 100644 runtime/python-executor/datamate/common/utils/llm_request.py create mode 100644 runtime/python-executor/datamate/common/utils/load_offline_module.py create mode 100644 runtime/python-executor/datamate/common/utils/registry.py create mode 100644 runtime/python-executor/datamate/common/utils/text_splitter.py create mode 100644 runtime/python-executor/datamate/core/__init__.py create mode 100644 runtime/python-executor/datamate/core/base_op.py create mode 100644 runtime/python-executor/datamate/core/constant.py create mode 100644 runtime/python-executor/datamate/core/dataset.py create mode 100644 runtime/python-executor/datamate/operator_runtime.py create mode 100644 runtime/python-executor/datamate/ops/__init__.py create mode 100644 runtime/python-executor/datamate/scheduler/__init__.py create mode 100644 runtime/python-executor/datamate/scheduler/cmd_task_scheduler.py create mode 100644 runtime/python-executor/datamate/scheduler/func_task_scheduler.py create mode 100644 runtime/python-executor/datamate/scheduler/scheduler.py create mode 100644 runtime/python-executor/datamate/sql_manager/__init__.py create mode 100644 runtime/python-executor/datamate/sql_manager/persistence_atction.py create mode 100644 runtime/python-executor/datamate/sql_manager/sql/sql_config.json create mode 100644 runtime/python-executor/datamate/sql_manager/sql_manager.py create mode 100644 runtime/python-executor/datamate/wrappers/__init__.py create mode 100644 runtime/python-executor/datamate/wrappers/data_juicer_wrapper.py create mode 100644 runtime/python-executor/datamate/wrappers/datamate_executor.py create mode 100644 runtime/python-executor/datamate/wrappers/datamate_wrapper.py create mode 100644 runtime/python-executor/pyproject.toml create mode 100644 scripts/db/00-database-init.sql create mode 100644 scripts/db/data-cleaning-init.sql create mode 100644 scripts/db/data-collection-init.sql create mode 100644 scripts/db/data-common-init.sql create mode 100644 scripts/db/data-management-init.sql create mode 100644 scripts/db/data-operator-init.sql create mode 100644 scripts/images/backend/Dockerfile create mode 100644 scripts/images/backend/settings.xml create mode 100644 scripts/images/backend/start.sh create mode 100644 scripts/images/datax/Dockerfile create mode 100644 scripts/images/datax/app.py create mode 100644 scripts/images/frontend/Dockerfile create mode 100644 scripts/images/frontend/edm.conf create mode 100644 scripts/images/mineru/Dockerfile create mode 100644 scripts/images/runtime/Dockerfile create mode 100644 scripts/images/unstructured/Dockerfile create mode 100644 scripts/images/unstructured/app.py create mode 100644 scripts/save_images.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..65ede07 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{java,kt}] +indent_size = 4 + +[*.{py}] +indent_size = 4 + +[*.{md}] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.github/workflows/docker-image-backend.yml b/.github/workflows/docker-image-backend.yml new file mode 100644 index 0000000..9548050 --- /dev/null +++ b/.github/workflows/docker-image-backend.yml @@ -0,0 +1,27 @@ +name: Backend Docker Image CI + +on: + push: + branches: [ "develop_930" ] + paths: + - 'backend/**' + - 'scripts/images/backend/**' + - '.github/workflows/docker-image-backend.yml' + pull_request: + branches: [ "develop_930" ] + paths: + - 'backend/**' + - 'scripts/images/backend/**' + - '.github/workflows/docker-image-backend.yml' + workflow_dispatch: + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Backend Docker image + run: make build-backend diff --git a/.github/workflows/docker-image-frontend.yml b/.github/workflows/docker-image-frontend.yml new file mode 100644 index 0000000..2e0fb3f --- /dev/null +++ b/.github/workflows/docker-image-frontend.yml @@ -0,0 +1,27 @@ +name: Frontend Docker Image CI + +on: + push: + branches: [ "develop_930" ] + paths: + - 'frontend/**' + - 'scripts/images/frontend/**' + - '.github/workflows/docker-image-frontend.yml' + pull_request: + branches: [ "develop_930" ] + paths: + - 'frontend/**' + - 'scripts/images/frontend/**' + - '.github/workflows/docker-image-frontend.yml' + workflow_dispatch: + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Frontend Docker image + run: make build-frontend diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0136fb7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,189 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# Java +*.class +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Gradle +.gradle +build/ +!gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +# IntelliJ IDEA +.idea +*.iws +*.iml +*.ipr +out/ + +# Eclipse +.project +.classpath +.c9/ +*.launch +.settings/ +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyEnv +.python-version + +# pipenv +Pipfile.lock + +# Celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Docker +*.dockerignore + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# IDE +.vscode/ +*.sublime-project +*.sublime-workspace diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..caad1ca --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +# DataMate Open Source License + +DataMate is licensed under the MIT License, with the following additional conditions: + +DataMate is permitted to be used commercially, including as a backend service for other applications or as an application development platform for enterprises. However, when the following conditions are met, you must contact the producer to obtain a commercial license: + +a. Multi-tenant SaaS service: Unless explicitly authorized by DataMate in writing, you may not use the DataMate source code to operate a multi-tenant SaaS service. +b. LOGO and copyright information: In the process of using DataMate's frontend, you may not remove or modify the LOGO or copyright information in the DataMate console or applications. This restriction is inapplicable to uses of Nexent that do not involve its frontend. + +Please contact zhangyafeng2@huawei.com by email to inquire about licensing matters. + +As a contributor, you should agree that: + +a. The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary. +b. Your contributed code may be used for commercial purposes, such as DataMate's cloud business. + +Apart from the specific conditions mentioned above, all other rights and restrictions follow the MIT License. +Detailed information about the MIT License can be found at: https://opensource.org/licenses/MIT + +Copyright © 2025 Huawei Technologies Co., Ltd. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6c453f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,164 @@ +MAKEFLAGS += --no-print-directory + +VERSION ?= latest +NAMESPACE ?= datamate + +.PHONY: build-% +build-%: + $(MAKE) $*-docker-build + +.PHONY: build +build: backend-docker-build frontend-docker-build runtime-docker-build + +.PHONY: create-namespace +create-namespace: + @kubectl get namespace $(NAMESPACE) > /dev/null 2>&1 || kubectl create namespace $(NAMESPACE) + +.PHONY: install-% +install-%: +ifeq ($(origin INSTALLER), undefined) + @echo "Choose a deployment method:" + @echo "1. Docker" + @echo "2. Kubernetes/Helm" + @echo -n "Enter choice: " + @read choice; \ + case $$choice in \ + 1) INSTALLER=docker ;; \ + 2) INSTALLER=k8s ;; \ + *) echo "Invalid choice" && exit 1 ;; \ + esac; \ + $(MAKE) $*-$$INSTALLER-install +else + $(MAKE) $*-$(INSTALLER)-install +endif + +.PHONY: install +install: install-data-mate + +.PHONY: uninstall-% +uninstall-%: +ifeq ($(origin INSTALLER), undefined) + @echo "Choose a deployment method:" + @echo "1. Docker" + @echo "2. Kubernetes/Helm" + @echo -n "Enter choice: " + @read choice; \ + case $$choice in \ + 1) INSTALLER=docker ;; \ + 2) INSTALLER=k8s ;; \ + *) echo "Invalid choice" && exit 1 ;; \ + esac; \ + $(MAKE) $*-$$INSTALLER-uninstall +else + $(MAKE) $*-$(INSTALLER)-uninstall +endif + +.PHONY: uninstall +uninstall: uninstall-data-mate + +# build +.PHONY: mineru-docker-build +mineru-docker-build: + docker build -t mineru:$(VERSION) . -f scripts/images/mineru/Dockerfile + +.PHONY: datax-docker-build +datax-docker-build: + docker build -t datax:$(VERSION) . -f scripts/images/datax/Dockerfile + +.PHONY: unstructured-docker-build +unstructured-docker-build: + docker build -t unstructured:$(VERSION) . -f scripts/images/unstructured/Dockerfile + +.PHONY: backend-docker-build +backend-docker-build: + docker build -t backend:$(VERSION) . -f scripts/images/backend/Dockerfile + +.PHONY: frontend-docker-build +frontend-docker-build: + docker build -t frontend:$(VERSION) . -f scripts/images/frontend/Dockerfile + +.PHONY: runtime-docker-build +runtime-docker-build: + docker build -t runtime:$(VERSION) . -f scripts/images/runtime/Dockerfile + +.PHONY: backend-docker-install +backend-docker-install: + cd deployment/docker/data-mate && docker-compose up -d backend + +.PHONY: backend-docker-uninstall +backend-docker-uninstall: + cd deployment/docker/data-mate && docker-compose down backend + +.PHONY: frontend-docker-install +frontend-docker-install: + cd deployment/docker/data-mate && docker-compose up -d frontend + +.PHONY: frontend-docker-uninstall +frontend-docker-uninstall: + cd deployment/docker/data-mate && docker-compose down frontend + +.PHONY: runtime-docker-install +runtime-docker-install: + cd deployment/docker/data-mate && docker-compose up -d runtime + +.PHONY: runtime-docker-uninstall +runtime-docker-uninstall: + cd deployment/docker/data-mate && docker-compose down runtime + +.PHONY: runtime-k8s-install +runtime-k8s-install: create-namespace + helm upgrade kuberay-operator deployment/helm/ray/kuberay-operator --install -n $(NAMESPACE) + helm upgrade raycluster deployment/helm/ray/ray-cluster/ --install -n $(NAMESPACE) + kubectl apply -f deployment/helm/ray/service.yaml -n $(NAMESPACE) + +.PHONY: runtime-k8s-uninstall +runtime-k8s-uninstall: + helm uninstall raycluster -n $(NAMESPACE) + helm uninstall kuberay-operator -n $(NAMESPACE) + kubectl delete -f deployment/helm/ray/service.yaml -n $(NAMESPACE) + +.PHONY: unstructured-k8s-install +unstructured-k8s-install: create-namespace + kubectl apply -f deployment/kubernetes/unstructured/deploy.yaml -n $(NAMESPACE) + +.PHONY: mysql-k8s-install +mysql-k8s-install: create-namespace + kubectl create configmap init-sql --from-file=scripts/db/ --dry-run=client -o yaml | kubectl apply -f - -n $(NAMESPACE) + kubectl apply -f deployment/kubernetes/mysql/configmap.yaml -n $(NAMESPACE) + kubectl apply -f deployment/kubernetes/mysql/deploy.yaml -n $(NAMESPACE) + +.PHONY: mysql-k8s-uninstall +mysql-k8s-uninstall: + kubectl delete configmap init-sql -n $(NAMESPACE) + kubectl delete -f deployment/kubernetes/mysql/configmap.yaml -n $(NAMESPACE) + kubectl delete -f deployment/kubernetes/mysql/deploy.yaml -n $(NAMESPACE) + +.PHONY: backend-k8s-install +backend-k8s-install: create-namespace + kubectl apply -f deployment/kubernetes/backend/deploy.yaml -n $(NAMESPACE) + +.PHONY: backend-k8s-uninstall +backend-k8s-uninstall: + kubectl delete -f deployment/kubernetes/backend/deploy.yaml -n $(NAMESPACE) + +.PHONY: frontend-k8s-install +frontend-k8s-install: create-namespace + kubectl apply -f deployment/kubernetes/frontend/deploy.yaml -n $(NAMESPACE) + +.PHONY: frontend-k8s-uninstall +frontend-k8s-uninstall: + kubectl delete -f deployment/kubernetes/frontend/deploy.yaml -n $(NAMESPACE) + +.PHONY: data-mate-docker-install +data-mate-docker-install: + cd deployment/docker/datamate && docker-compose up -d + +.PHONY: data-mate-docker-uninstall +data-mate-docker-uninstall: + cd deployment/docker/datamate && docker-compose down + +.PHONY: data-mate-k8s-install +data-mate-k8s-install: create-namespace mysql-k8s-install backend-k8s-install frontend-k8s-install runtime-k8s-install + +.PHONY: data-mate-k8s-uninstall +data-mate-k8s-uninstall: mysql-k8s-uninstall backend-k8s-uninstall frontend-k8s-uninstall runtime-k8s-uninstall diff --git a/README-zh.md b/README-zh.md new file mode 100644 index 0000000..b135be8 --- /dev/null +++ b/README-zh.md @@ -0,0 +1,72 @@ +# DataMate 一站式数据工作平台 + +
+ +[![Backend CI](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-backend.yml/badge.svg)](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-backend.yml) +[![Frontend CI](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-frontend.yml/badge.svg)](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-frontend.yml) +![GitHub Stars](https://img.shields.io/github/stars/ModelEngine-Group/DataMate) +![GitHub Forks](https://img.shields.io/github/forks/ModelEngine-Group/DataMate) +![GitHub Issues](https://img.shields.io/github/issues/ModelEngine-Group/DataMate) +![GitHub License](https://img.shields.io/github/license/ModelEngine-Group/DataMate) + +**DataMate是面向模型微调与RAG检索的企业级数据处理平台,支持数据归集、数据管理、算子市场、数据清洗、数据合成、数据标注、数据评估、知识生成等核心功能。 +** + +[简体中文](./README-zh.md) | [English](./README.md) + +如果您喜欢这个项目,希望您能给我们一个Star⭐️! + +
+ +## 🌟 核心特性 + +- **核心模块**:数据归集、数据管理、算子市场、数据清洗、数据合成、数据标注、数据评估、知识生成 +- **可视化编排**:拖拽式数据处理流程设计 +- **算子生态**:丰富的内置算子和自定义算子支持 + +## 🚀 快速开始 + +### 前置条件 + +- Git (用于拉取源码) +- Make (用于构建和安装) +- Docker (用于构建镜像和部署服务) +- Docker-Compose (用于部署服务-docker方式) +- kubernetes (用于部署服务-k8s方式) +- Helm (用于部署服务-k8s方式) + +### 拉取代码 + +```bash +git clone git@github.com:ModelEngine-Group/DataMate.git +``` + +### 镜像构建 + +```bash +make build +``` + +### Docker安装 + +```bash +make install INSTALLER=docker +``` + +### kubernetes安装 + +```bash +make install INSTALLER=k8s +``` + +## 🤝 贡献指南 + +感谢您对本项目的关注!我们非常欢迎社区的贡献,无论是提交 Bug 报告、提出功能建议,还是直接参与代码开发,都能帮助项目变得更好。 + +• 📮 [GitHub Issues](../../issues):提交 Bug 或功能建议。 + +• 🔧 [GitHub Pull Requests](../../pulls):贡献代码改进。 + +## 📄 许可证 + +DataMate 基于 [MIT](LICENSE) 开源,您可以在遵守许可证条款的前提下自由使用、修改和分发本项目的代码。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..b80be8d --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# DataMate All-in-One Data Work Platform + +
+ +[![Backend CI](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-backend.yml/badge.svg)](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-backend.yml) +[![Frontend CI](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-frontend.yml/badge.svg)](https://github.com/ModelEngine-Group/DataMate/actions/workflows/docker-image-frontend.yml) +![GitHub Stars](https://img.shields.io/github/stars/ModelEngine-Group/DataMate) +![GitHub Forks](https://img.shields.io/github/forks/ModelEngine-Group/DataMate) +![GitHub Issues](https://img.shields.io/github/issues/ModelEngine-Group/DataMate) +![GitHub License](https://img.shields.io/github/license/ModelEngine-Group/DataMate) + +**DataMate is an enterprise-level data processing platform for model fine-tuning and RAG retrieval, supporting core +functions such as data collection, data management, operator marketplace, data cleaning, data synthesis, data +annotation, data evaluation, and knowledge generation.** + +[简体中文](./README-zh.md) | [English](./README.md) + +If you like this project, please give it a Star⭐️! + +
+ +## 🌟 Core Features + +- **Core Modules**: Data Collection, Data Management, Operator Marketplace, Data Cleaning, Data Synthesis, Data + Annotation, Data Evaluation, Knowledge Generation. +- **Visual Orchestration**: Drag-and-drop data processing workflow design. +- **Operator Ecosystem**: Rich built-in operators and support for custom operators. + +## 🚀 Quick Start + +### Prerequisites + +- Git (for pulling source code) +- Make (for building and installing) +- Docker (for building images and deploying services) +- Docker-Compose (for service deployment - Docker method) +- Kubernetes (for service deployment - k8s method) +- Helm (for service deployment - k8s method) + +### Clone the Code + +```bash +git clone git@github.com:ModelEngine-Group/DataMate.git +``` + +### Build Images + +```bash +make build +``` + +### Docker Installation + +```bash +make install INSTALLER=docker +``` + +### Kubernetes Installation + +```bash +make install INSTALLER=k8s +``` + +## 🤝 Contribution Guidelines + +Thank you for your interest in this project! We warmly welcome contributions from the community. Whether it's submitting +bug reports, suggesting new features, or directly participating in code development, all forms of help make the project +better. + +• 📮 [GitHub Issues](../../issues): Submit bugs or feature suggestions. + +• 🔧 [GitHub Pull Requests](../../pulls): Contribute code improvements. + +## 📄 License + +DataMate is open source under the [MIT](LICENSE) license. You are free to use, modify, and distribute the code of this +project in compliance with the license terms. diff --git a/backend/api-gateway/pom.xml b/backend/api-gateway/pom.xml new file mode 100644 index 0000000..c6284e6 --- /dev/null +++ b/backend/api-gateway/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../pom.xml + + + api-gateway + API Gateway + API网关服务 + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/backend/api-gateway/src/main/java/com/datamate/gateway/ApiGatewayApplication.java b/backend/api-gateway/src/main/java/com/datamate/gateway/ApiGatewayApplication.java new file mode 100644 index 0000000..a9073ce --- /dev/null +++ b/backend/api-gateway/src/main/java/com/datamate/gateway/ApiGatewayApplication.java @@ -0,0 +1,77 @@ +package com.datamate.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; + +/** + * API Gateway & Auth Service Application + * 统一的API网关和认证授权微服务 + * 提供路由、鉴权、限流等功能 + */ +@SpringBootApplication +@ComponentScan(basePackages = { + "com.datamate.gateway", + "com.datamate.shared" +}) +public class ApiGatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(ApiGatewayApplication.class, args); + } + + @Bean + public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + // 数据归集服务路由 + .route("data-collection", r -> r.path("/api/data-collection/**") + .uri("lb://data-collection-service")) + + // 数据管理服务路由 + .route("data-management", r -> r.path("/api/data-management/**") + .uri("lb://data-management-service")) + + // 算子市场服务路由 + .route("operator-market", r -> r.path("/api/operators/**") + .uri("lb://operator-market-service")) + + // 数据清洗服务路由 + .route("data-cleaning", r -> r.path("/api/cleaning/**") + .uri("lb://data-cleaning-service")) + + // 数据合成服务路由 + .route("data-synthesis", r -> r.path("/api/synthesis/**") + .uri("lb://data-synthesis-service")) + + // 数据标注服务路由 + .route("data-annotation", r -> r.path("/api/annotation/**") + .uri("lb://data-annotation-service")) + + // 数据评估服务路由 + .route("data-evaluation", r -> r.path("/api/evaluation/**") + .uri("lb://data-evaluation-service")) + + // 流程编排服务路由 + .route("pipeline-orchestration", r -> r.path("/api/pipelines/**") + .uri("lb://pipeline-orchestration-service")) + + // 执行引擎服务路由 + .route("execution-engine", r -> r.path("/api/execution/**") + .uri("lb://execution-engine-service")) + + // 认证服务路由 + .route("auth-service", r -> r.path("/api/auth/**") + .uri("lb://auth-service")) + + // RAG服务路由 + .route("rag-indexer", r -> r.path("/api/rag/indexer/**") + .uri("lb://rag-indexer-service")) + .route("rag-query", r -> r.path("/api/rag/query/**") + .uri("lb://rag-query-service")) + + .build(); + } +} diff --git a/backend/openapi/README.md b/backend/openapi/README.md new file mode 100644 index 0000000..18fbe63 --- /dev/null +++ b/backend/openapi/README.md @@ -0,0 +1,147 @@ +# OpenAPI Code Generation Configuration +# 基于YAML生成API代码的配置文件 + +## Maven Plugin Configuration for Spring Boot +# 在各个服务的pom.xml中添加以下插件配置: + +```xml + + + org.openapitools + openapi-generator-maven-plugin + 6.6.0 + + + + generate + + + ${project.basedir}/../../openapi/specs/${project.artifactId}.yaml + spring + ${project.build.directory}/generated-sources/openapi + com.datamate.${project.name}.interfaces.api + com.datamate.${project.name}.interfaces.dto + + true + true + true + true + true + java8 + true + true + true + springdoc + + + + + +``` + +## Gradle Plugin Configuration (Alternative) +# 如果使用Gradle,可以使用以下配置: + +```gradle +plugins { + id 'org.openapi.generator' version '6.6.0' +} + +openApiGenerate { + generatorName = "spring" + inputSpec = "$rootDir/openapi/specs/${project.name}.yaml" + outputDir = "$buildDir/generated-sources/openapi" + apiPackage = "com.datamate.${project.name}.interfaces.api" + modelPackage = "com.datamate.${project.name}.interfaces.dto" + configOptions = [ + interfaceOnly: "true", + useTags: "true", + skipDefaultInterface: "true", + hideGenerationTimestamp: "true", + java8: "true", + dateLibrary: "java8", + useBeanValidation: "true", + performBeanValidation: "true", + useSpringBoot3: "true", + documentationProvider: "springdoc" + ] +} +``` + +## Frontend TypeScript Client Generation +# 为前端生成TypeScript客户端: + +```bash +# 安装 OpenAPI Generator CLI +npm install -g @openapitools/openapi-generator-cli + +# 生成TypeScript客户端 +openapi-generator-cli generate \ + -i openapi/specs/data-annotation-service.yaml \ + -g typescript-axios \ + -o frontend/packages/api-client/src/generated/annotation \ + --additional-properties=supportsES6=true,npmName=@datamate/annotation-api,npmVersion=1.0.0 +``` + +## Usage in Services +# 在各个服务中使用生成的代码: + +1. **在 interfaces 层实现生成的API接口**: +```java +@RestController +@RequestMapping("/api/v1/annotation") +public class AnnotationTaskController implements AnnotationTasksApi { + + private final AnnotationTaskApplicationService annotationTaskService; + + @Override + public ResponseEntity getAnnotationTasks( + Integer page, Integer size, String status) { + // 实现业务逻辑 + return ResponseEntity.ok(annotationTaskService.getTasks(page, size, status)); + } +} +``` + +2. **在 application 层使用生成的DTO**: +```java +@Service +public class AnnotationTaskApplicationService { + + public AnnotationTaskPageResponse getTasks(Integer page, Integer size, String status) { + // 业务逻辑实现 + // 使用生成的DTO类型 + } +} +``` + +## Build Integration +# 构建集成脚本位置:scripts/build/generate-api.sh + +```bash +#!/bin/bash +# 生成所有服务的API代码 + +OPENAPI_DIR="openapi/specs" +SERVICES=( + "data-annotation-service" + "data-management-service" + "operator-market-service" + "data-cleaning-service" + "data-synthesis-service" + "data-evaluation-service" + "pipeline-orchestration-service" + "execution-engine-service" + "rag-indexer-service" + "rag-query-service" + "api-gateway" + "auth-service" +) + +for service in "${SERVICES[@]}"; do + echo "Generating API for $service..." + mvn -f backend/services/$service/pom.xml openapi-generator:generate +done + +echo "All APIs generated successfully!" +``` diff --git a/backend/openapi/specs/data-annotation.yaml b/backend/openapi/specs/data-annotation.yaml new file mode 100644 index 0000000..771d8a5 --- /dev/null +++ b/backend/openapi/specs/data-annotation.yaml @@ -0,0 +1,298 @@ +openapi: 3.0.3 +info: + title: Data Annotation Service API + description: 数据标注服务API - 智能预标注、人工平台、主动学习 + version: 1.0.0 + contact: + name: Data Mate Platform Team + +servers: + - url: http://localhost:8080 + description: Development server + +tags: + - name: annotation-tasks + description: 标注任务管理 + - name: annotation-data + description: 标注数据管理 + - name: pre-annotation + description: 智能预标注 + - name: active-learning + description: 主动学习 + +paths: + /api/v1/annotation/tasks: + get: + tags: + - annotation-tasks + summary: 获取标注任务列表 + description: 分页获取标注任务列表 + parameters: + - name: page + in: query + description: 页码 + schema: + type: integer + default: 0 + - name: size + in: query + description: 每页大小 + schema: + type: integer + default: 20 + - name: status + in: query + description: 任务状态 + schema: + type: string + enum: [PENDING, IN_PROGRESS, COMPLETED, PAUSED] + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/AnnotationTaskPageResponse' + '400': + description: 请求参数错误 + '500': + description: 服务器内部错误 + + post: + tags: + - annotation-tasks + summary: 创建标注任务 + description: 创建新的标注任务 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateAnnotationTaskRequest' + responses: + '201': + description: 创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/AnnotationTaskResponse' + '400': + description: 请求参数错误 + '500': + description: 服务器内部错误 + + /api/v1/annotation/tasks/{taskId}: + get: + tags: + - annotation-tasks + summary: 获取标注任务详情 + parameters: + - name: taskId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/AnnotationTaskResponse' + '404': + description: 任务不存在 + + put: + tags: + - annotation-tasks + summary: 更新标注任务 + parameters: + - name: taskId + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateAnnotationTaskRequest' + responses: + '200': + description: 更新成功 + content: + application/json: + schema: + $ref: '#/components/schemas/AnnotationTaskResponse' + + /api/v1/annotation/pre-annotate: + post: + tags: + - pre-annotation + summary: 智能预标注 + description: 使用AI模型进行智能预标注 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PreAnnotationRequest' + responses: + '200': + description: 预标注成功 + content: + application/json: + schema: + $ref: '#/components/schemas/PreAnnotationResponse' + +components: + schemas: + AnnotationTaskResponse: + type: object + properties: + id: + type: string + description: 任务ID + name: + type: string + description: 任务名称 + description: + type: string + description: 任务描述 + type: + type: string + enum: [TEXT_CLASSIFICATION, NAMED_ENTITY_RECOGNITION, OBJECT_DETECTION, SEMANTIC_SEGMENTATION] + description: 标注类型 + status: + type: string + enum: [PENDING, IN_PROGRESS, COMPLETED, PAUSED] + description: 任务状态 + datasetId: + type: string + description: 数据集ID + progress: + type: number + format: double + description: 进度百分比 + createdAt: + type: string + format: date-time + description: 创建时间 + updatedAt: + type: string + format: date-time + description: 更新时间 + + CreateAnnotationTaskRequest: + type: object + required: + - name + - type + - datasetId + properties: + name: + type: string + description: 任务名称 + description: + type: string + description: 任务描述 + type: + type: string + enum: [TEXT_CLASSIFICATION, NAMED_ENTITY_RECOGNITION, OBJECT_DETECTION, SEMANTIC_SEGMENTATION] + description: 标注类型 + datasetId: + type: string + description: 数据集ID + configuration: + type: object + description: 标注配置 + + UpdateAnnotationTaskRequest: + type: object + properties: + name: + type: string + description: 任务名称 + description: + type: string + description: 任务描述 + status: + type: string + enum: [PENDING, IN_PROGRESS, COMPLETED, PAUSED] + description: 任务状态 + + AnnotationTaskPageResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/AnnotationTaskResponse' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + size: + type: integer + number: + type: integer + + PreAnnotationRequest: + type: object + required: + - taskId + - dataIds + properties: + taskId: + type: string + description: 标注任务ID + dataIds: + type: array + items: + type: string + description: 待预标注的数据ID列表 + modelId: + type: string + description: 预标注模型ID + confidence: + type: number + format: double + description: 置信度阈值 + + PreAnnotationResponse: + type: object + properties: + taskId: + type: string + description: 任务ID + processedCount: + type: integer + description: 已处理数据数量 + successCount: + type: integer + description: 成功预标注数量 + results: + type: array + items: + type: object + properties: + dataId: + type: string + annotations: + type: array + items: + type: object + confidence: + type: number + format: double + + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + +security: + - BearerAuth: [] diff --git a/backend/openapi/specs/data-cleaning.yaml b/backend/openapi/specs/data-cleaning.yaml new file mode 100644 index 0000000..ff49274 --- /dev/null +++ b/backend/openapi/specs/data-cleaning.yaml @@ -0,0 +1,491 @@ +openapi: 3.0.3 +info: + title: Data Cleaning Service API + description: 数据清洗服务API - 策略/规则、流程编排对接 + version: 1.0.0 + contact: + name: Data Mate Platform Team + +servers: + - url: http://localhost:8084 + description: Development server + +tags: + - name: CleaningTask + description: 数据清洗任务管理 + - name: CleaningTemplate + description: 数据清洗模板管理 + +paths: + /ray/log: + get: + summary: 获取ray日志文件 + deprecated: false + description: '' + tags: [ ] + parameters: [ ] + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: { } + headers: { } + security: [ ] + /cleaning/tasks: + get: + summary: 查询数据清洗任务列表 + deprecated: false + description: 获取所有数据清洗任务或根据查询参数筛选任务。 + tags: + - CleaningTask + parameters: + - name: status + in: query + description: 根据任务状态筛选 (e.g., pending, running, completed, failed) + required: false + schema: + type: string + - name: keywords + in: query + description: 关键字 + required: false + schema: + type: string + - name: page + in: query + description: 分页数 + required: true + schema: + type: integer + - name: size + in: query + description: 分页单页数 + required: true + schema: + type: integer + responses: + '200': + description: 成功获取任务列表 + content: + application/json: + schema: + type: array + items: &ref_1 + $ref: '#/components/schemas/CleaningTask' + headers: { } + security: [ ] + post: + summary: 创建新的数据清洗任务 + deprecated: false + description: 可以直接创建任务或基于现有模板创建任务。 + tags: + - CleaningTask + parameters: [ ] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateCleaningTaskRequest' + examples: { } + responses: + '201': + description: 任务创建成功 + content: + application/json: + schema: *ref_1 + headers: { } + security: [ ] + /cleaning/tasks/{taskId}: + get: + summary: 获取单个数据清洗任务详情 + deprecated: false + description: 根据任务ID获取任务的详细信息。 + tags: + - CleaningTask + parameters: + - name: taskId + in: path + description: 任务的唯一标识符 + required: true + example: '' + schema: + type: string + responses: + '200': + description: 成功获取任务详情 + content: + application/json: + schema: *ref_1 + headers: { } + security: [ ] + delete: + summary: 删除数据清洗任务 + deprecated: false + description: 根据任务ID删除指定的任务。 + tags: + - CleaningTask + parameters: + - name: taskId + in: path + description: 任务的唯一标识符 + required: true + example: '' + schema: + type: string + responses: + '204': + description: 任务删除成功 + headers: { } + security: [ ] + /cleaning/templates: + get: + summary: 查询数据清洗模板列表 + deprecated: false + description: 获取所有可用的数据清洗模板。 + tags: + - CleaningTemplate + parameters: [ ] + responses: + '200': + description: 成功获取模板列表 + content: + application/json: + schema: + type: array + items: &ref_2 + $ref: '#/components/schemas/CleaningTemplate' + headers: { } + security: [ ] + post: + summary: 创建新的数据清洗模板 + deprecated: false + description: 定义一个新的数据清洗模板。 + tags: + - CleaningTemplate + parameters: [ ] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateCleaningTemplateRequest' + responses: + '201': + description: 模板创建成功 + content: + application/json: + schema: *ref_2 + headers: { } + security: [ ] + /cleaning/templates/{templateId}: + get: + summary: 获取单个数据清洗模板详情 + deprecated: false + description: 根据模板ID获取模板的详细信息。 + tags: + - CleaningTemplate + parameters: + - name: templateId + in: path + description: 模板的唯一标识符 + required: true + example: '' + schema: + type: string + responses: + '200': + description: 成功获取模板详情 + content: + application/json: + schema: *ref_2 + headers: { } + security: [ ] + put: + summary: 更新数据清洗模板 + deprecated: false + description: 根据模板ID更新模板的全部信息。 + tags: + - CleaningTemplate + parameters: + - name: templateId + in: path + description: 模板的唯一标识符 + required: true + example: '' + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateCleaningTemplateRequest' + responses: + '200': + description: 模板更新成功 + content: + application/json: + schema: *ref_2 + headers: { } + security: [ ] + delete: + summary: 删除数据清洗模板 + deprecated: false + description: 根据模板ID删除指定的模板。 + tags: + - CleaningTemplate + parameters: + - name: templateId + in: path + description: 模板的唯一标识符 + required: true + example: '' + schema: + type: string + responses: + '204': + description: 模板删除成功 + headers: { } + security: [ ] + +components: + schemas: + OperatorInstance: + type: object + properties: + id: + type: string + overrides: + type: object + properties: { } + additionalProperties: + type: object + properties: { } + required: + - id + - overrides + CleaningProcess: + type: object + properties: + process: + type: number + format: float + description: 进度百分比 + totalFileNum: + type: integer + description: 总文件数量 + finishedFileNum: + type: integer + description: 已完成文件数量 + required: + - process + - totalFileNum + - finishedFileNum + OperatorResponse: + type: object + properties: + id: + type: string + description: 算子ID + name: + type: string + description: 算子名称 + description: + type: string + description: 算子描述 + version: + type: string + description: 算子版本 + inputs: + type: string + description: 输入类型 + outputs: + type: string + description: 输入类型 + runtime: + type: string + description: 运行时设置 + settings: + type: string + description: 算子参数 + isStar: + type: boolean + description: 是否收藏 + createdAt: + type: string + format: date-time + description: 创建时间 + updatedAt: + type: string + format: date-time + description: 更新时间 + required: + - inputs + - outputs + - runtime + - settings + - isStar + UpdateCleaningTemplateRequest: + type: object + required: + - name + - instance + - id + properties: + id: + type: string + name: + type: string + description: 模板名称 + description: + type: string + description: 模板描述 + instance: + type: array + items: &ref_3 + $ref: '#/components/schemas/OperatorInstance' + description: 模板定义的清洗规则和配置 + CreateCleaningTemplateRequest: + type: object + required: + - name + - instance + properties: + name: + type: string + description: 模板名称 + description: + type: string + description: 模板描述 + instance: + type: array + items: *ref_3 + description: 任务的具体配置(如果非模板创建,则直接定义)' + CleaningTemplate: + type: object + required: + - id + - name + - instance + - createdAt + properties: + id: + type: string + description: 模板唯一标识符 + name: + type: string + description: 模板名称 + description: + type: string + description: 模板描述 + instance: + type: array + items: &ref_4 + $ref: '#/components/schemas/OperatorResponse' + description: 模板定义的清洗规则和配置 + createdAt: + type: string + format: date-time + description: 模板创建时间 + updatedAt: + type: string + format: date-time + description: 模板最后更新时间 + CreateCleaningTaskRequest: + type: object + required: + - name + - instance + - srcDatasetId + - srcDatasetName + - destDatasetName + - destDatasetType + properties: + name: + type: string + description: 任务名称 + description: + type: string + description: 任务描述 + srcDatasetId: + type: string + srcDatasetName: + type: string + destDatasetName: + type: string + destDatasetType: + type: string + instance: + type: array + items: *ref_3 + description: 任务的具体配置(如果非模板创建,则直接定义) + ErrorResponse: + type: object + properties: + error: + type: string + description: 错误类型 + message: + type: string + description: 错误详细信息 + CleaningTask: + type: object + required: + - id + - name + - status + - createdAt + - startedAt + properties: + id: + type: string + description: 任务唯一标识符 + name: + type: string + description: 任务名称 + description: + type: string + description: 任务描述 + srcDatasetId: + type: string + description: 源数据集id + srcDatasetName: + type: string + description: 源数据集名称 + destDatasetId: + type: string + description: 目标数据集id + destDatasetName: + type: string + description: 目标数据集名称 + status: + type: string + description: 任务当前状态 + enum: + - pending + - running + - completed + - failed + templateId: + type: string + description: 关联的模板ID(如果基于模板创建) + instance: + type: array + items: *ref_4 + description: 任务的具体配置(如果非模板创建,则直接定义) + progress: + $ref: '#/components/schemas/CleaningProcess' + createdAt: + type: string + description: 任务创建时间 + format: date-time + startedAt: + type: string + format: date-time + description: 任务开始时间 + finishedAt: + type: string + format: date-time + description: 任务最后更新时间 + securitySchemes: { } diff --git a/backend/openapi/specs/data-collection.yaml b/backend/openapi/specs/data-collection.yaml new file mode 100644 index 0000000..cc2731f --- /dev/null +++ b/backend/openapi/specs/data-collection.yaml @@ -0,0 +1,517 @@ +openapi: 3.0.3 +info: + title: Data Collection Service API + description: | + 数据归集服务API,基于数据归集实现数据采集和归集功能。 + + 主要功能: + - 数据归集任务创建和管理 + - 数据同步任务执行 + - 任务监控和状态查询 + - 执行日志查看 + + version: 1.0.0 + +servers: + - url: http://localhost:8090/api/v1/collection + description: Development server + +tags: + - name: CollectionTask + description: 数据归集任务管理(包括模板查询) + - name: TaskExecution + description: 任务执行管理 + +paths: + /data-collection/tasks: + get: + operationId: getTasks + tags: [CollectionTask] + summary: 获取归集任务列表 + parameters: + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + - name: status + in: query + schema: + $ref: '#/components/schemas/TaskStatus' + - name: name + in: query + description: 任务名称关键字搜索 + schema: + type: string + responses: + '200': + description: 归集任务列表 + content: + application/json: + schema: + $ref: '#/components/schemas/PagedCollectionTaskSummary' + + post: + operationId: createTask + tags: [CollectionTask] + summary: 创建归集任务 + description: 创建新的数据归集任务 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateCollectionTaskRequest' + responses: + '201': + description: 归集任务创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionTaskResponse' + + /data-collection/tasks/{id}: + get: + operationId: getTaskDetail + tags: [CollectionTask] + summary: 获取归集任务详情 + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: 归集任务详情 + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionTaskResponse' + '404': + description: 归集任务不存在 + + put: + operationId: updateTask + tags: [CollectionTask] + summary: 更新归集任务 + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateCollectionTaskRequest' + responses: + '200': + description: 归集任务更新成功 + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionTaskResponse' + + delete: + operationId: deleteTask + tags: [CollectionTask] + summary: 删除归集任务 + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '204': + description: 归集任务删除成功 + + /tasks/{id}/execute: + post: + tags: [TaskExecution] + summary: 执行归集任务 + description: 立即执行指定的归集任务 + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '201': + description: 任务执行已启动 + content: + application/json: + schema: + $ref: '#/components/schemas/TaskExecutionResponse' + + /tasks/{id}/executions: + get: + tags: [TaskExecution] + summary: 获取任务执行记录 + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + responses: + '200': + description: 任务执行记录列表 + content: + application/json: + schema: + $ref: '#/components/schemas/PagedTaskExecutions' + + /executions/{id}: + get: + tags: [TaskExecution] + summary: 获取执行详情 + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: 执行详情 + content: + application/json: + schema: + $ref: '#/components/schemas/TaskExecutionDetail' + + delete: + tags: [TaskExecution] + summary: 停止任务执行 + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '204': + description: 任务执行已停止 + + /templates: + get: + tags: [CollectionTask] + summary: 获取DataX模板列表 + description: 获取可用的DataX任务模板列表,用于创建任务时选择 + parameters: + - name: sourceType + in: query + description: 源数据源类型过滤 + schema: + type: string + - name: targetType + in: query + description: 目标数据源类型过滤 + schema: + type: string + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + responses: + '200': + description: 归集模板列表 + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDataxTemplates' + +components: + schemas: + TaskStatus: + type: string + enum: + - DRAFT + - READY + - RUNNING + - SUCCESS + - FAILED + - STOPPED + description: | + 任务和执行状态枚举: + - DRAFT: 草稿状态 + - READY: 就绪状态 + - RUNNING: 运行中 + - SUCCESS: 执行成功 (对应原来的COMPLETED/SUCCESS) + - FAILED: 执行失败 + - STOPPED: 已停止 + + SyncMode: + type: string + enum: [ONCE, SCHEDULED] + description: 同步方式:一次性(ONCE) 或 定时(SCHEDULED) + + CollectionTaskSummary: + type: object + properties: + id: + type: string + name: + type: string + description: + type: string + status: + $ref: '#/components/schemas/TaskStatus' + syncMode: + $ref: '#/components/schemas/SyncMode' + lastExecutionId: + type: string + description: 最后执行ID + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + description: 任务列表摘要信息(不包含详细配置与调度表达式) + + CollectionTaskResponse: + type: object + properties: + id: + type: string + name: + type: string + description: + type: string + config: + type: object + additionalProperties: true + description: 归集配置,包含源端和目标端配置信息 + status: + $ref: '#/components/schemas/TaskStatus' + syncMode: + $ref: '#/components/schemas/SyncMode' + scheduleExpression: + type: string + description: Cron调度表达式 (仅当 syncMode = SCHEDULED 时有效) + lastExecutionId: + type: string + description: 最后执行ID + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + + CreateCollectionTaskRequest: + type: object + required: + - name + - config + - syncMode + properties: + name: + type: string + description: 任务名称 + minLength: 1 + maxLength: 100 + description: + type: string + description: 任务描述 + maxLength: 500 + config: + type: object + description: 归集配置,包含源端和目标端配置信息 + additionalProperties: true + syncMode: + $ref: '#/components/schemas/SyncMode' + scheduleExpression: + type: string + description: Cron调度表达式 (syncMode=SCHEDULED 时必填) + + UpdateCollectionTaskRequest: + type: object + properties: + name: + type: string + description: 任务名称 + minLength: 1 + maxLength: 100 + description: + type: string + description: 任务描述 + maxLength: 500 + config: + type: object + description: 归集配置,包含源端和目标端配置信息 + additionalProperties: true + syncMode: + $ref: '#/components/schemas/SyncMode' + scheduleExpression: + type: string + description: Cron调度表达式 (syncMode=SCHEDULED 时必填) + + PagedCollectionTaskSummary: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/CollectionTaskSummary' + totalElements: + type: integer + totalPages: + type: integer + number: + type: integer + size: + type: integer + + PagedCollectionTasks: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/CollectionTaskResponse' + totalElements: + type: integer + totalPages: + type: integer + number: + type: integer + size: + type: integer + + TaskExecutionResponse: + type: object + properties: + id: + type: string + taskId: + type: string + taskName: + type: string + status: + $ref: '#/components/schemas/TaskStatus' + startedAt: + type: string + format: date-time + + TaskExecutionDetail: + type: object + properties: + id: + type: string + taskId: + type: string + taskName: + type: string + status: + $ref: '#/components/schemas/TaskStatus' + progress: + type: number + format: double + minimum: 0 + maximum: 100 + recordsTotal: + type: integer + recordsProcessed: + type: integer + recordsSuccess: + type: integer + recordsFailed: + type: integer + throughput: + type: number + format: double + dataSizeBytes: + type: integer + startedAt: + type: string + format: date-time + completedAt: + type: string + format: date-time + durationSeconds: + type: integer + errorMessage: + type: string + + PagedTaskExecutions: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/TaskExecutionDetail' + totalElements: + type: integer + totalPages: + type: integer + number: + type: integer + size: + type: integer + + DataxTemplateSummary: + type: object + properties: + id: + type: string + name: + type: string + sourceType: + type: string + description: 源数据源类型 + targetType: + type: string + description: 目标数据源类型 + description: + type: string + version: + type: string + isSystem: + type: boolean + description: 是否为系统模板 + createdAt: + type: string + format: date-time + + PagedDataxTemplates: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/DataxTemplateSummary' + totalElements: + type: integer + totalPages: + type: integer + number: + type: integer + size: + type: integer diff --git a/backend/openapi/specs/data-evaluation.yaml b/backend/openapi/specs/data-evaluation.yaml new file mode 100644 index 0000000..4a6b23a --- /dev/null +++ b/backend/openapi/specs/data-evaluation.yaml @@ -0,0 +1,630 @@ +openapi: 3.0.3 +info: + title: Data Evaluation Service API + description: 数据评估服务API - 质量、适配性、价值评估 + version: 1.0.0 + contact: + name: Data Mate Platform Team + +servers: + - url: http://localhost:8086 + description: Development server + +tags: + - name: quality-evaluation + description: 数据质量评估 + - name: compatibility-evaluation + description: 适配性评估 + - name: value-evaluation + description: 价值评估 + - name: evaluation-reports + description: 评估报告 + +paths: + /api/v1/evaluation/quality: + post: + tags: + - quality-evaluation + summary: 数据质量评估 + description: 对数据集进行质量评估,包括完整性、准确性、一致性等 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/QualityEvaluationRequest' + responses: + '200': + description: 评估成功 + content: + application/json: + schema: + $ref: '#/components/schemas/QualityEvaluationResponse' + + /api/v1/evaluation/quality/{evaluationId}: + get: + tags: + - quality-evaluation + summary: 获取质量评估结果 + parameters: + - name: evaluationId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/QualityEvaluationDetailResponse' + + /api/v1/evaluation/compatibility: + post: + tags: + - compatibility-evaluation + summary: 适配性评估 + description: 评估数据集与目标模型或任务的适配性 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CompatibilityEvaluationRequest' + responses: + '200': + description: 评估成功 + content: + application/json: + schema: + $ref: '#/components/schemas/CompatibilityEvaluationResponse' + + /api/v1/evaluation/value: + post: + tags: + - value-evaluation + summary: 价值评估 + description: 评估数据集的商业价值和使用价值 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ValueEvaluationRequest' + responses: + '200': + description: 评估成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ValueEvaluationResponse' + + /api/v1/evaluation/reports: + get: + tags: + - evaluation-reports + summary: 获取评估报告列表 + parameters: + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + - name: type + in: query + schema: + $ref: '#/components/schemas/EvaluationType' + - name: datasetId + in: query + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/EvaluationReportPageResponse' + + /api/v1/evaluation/reports/{reportId}: + get: + tags: + - evaluation-reports + summary: 获取评估报告详情 + parameters: + - name: reportId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/EvaluationReportDetailResponse' + + /api/v1/evaluation/reports/{reportId}/export: + get: + tags: + - evaluation-reports + summary: 导出评估报告 + parameters: + - name: reportId + in: path + required: true + schema: + type: string + - name: format + in: query + schema: + type: string + enum: [PDF, EXCEL, JSON] + default: PDF + responses: + '200': + description: 导出成功 + content: + application/octet-stream: + schema: + type: string + format: binary + + /api/v1/evaluation/batch: + post: + tags: + - evaluation-reports + summary: 批量评估 + description: 对多个数据集进行批量评估 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/BatchEvaluationRequest' + responses: + '202': + description: 批量评估任务已提交 + content: + application/json: + schema: + $ref: '#/components/schemas/BatchEvaluationResponse' + +components: + schemas: + QualityEvaluationRequest: + type: object + required: + - datasetId + - metrics + properties: + datasetId: + type: string + description: 数据集ID + metrics: + type: array + items: + $ref: '#/components/schemas/QualityMetric' + description: 评估指标 + sampleSize: + type: integer + description: 采样大小 + parameters: + type: object + description: 评估参数 + + QualityEvaluationResponse: + type: object + properties: + evaluationId: + type: string + status: + $ref: '#/components/schemas/EvaluationStatus' + overallScore: + type: number + format: double + description: 总体质量分数 + metrics: + type: array + items: + $ref: '#/components/schemas/QualityMetricResult' + recommendations: + type: array + items: + type: string + createdAt: + type: string + format: date-time + + QualityEvaluationDetailResponse: + allOf: + - $ref: '#/components/schemas/QualityEvaluationResponse' + - type: object + properties: + detailedResults: + $ref: '#/components/schemas/DetailedQualityResults' + visualizations: + type: array + items: + $ref: '#/components/schemas/VisualizationData' + + CompatibilityEvaluationRequest: + type: object + required: + - datasetId + - targetType + properties: + datasetId: + type: string + targetType: + $ref: '#/components/schemas/TargetType' + targetConfig: + type: object + description: 目标配置(模型、任务等) + evaluationCriteria: + type: array + items: + $ref: '#/components/schemas/CompatibilityCriterion' + + CompatibilityEvaluationResponse: + type: object + properties: + evaluationId: + type: string + compatibilityScore: + type: number + format: double + results: + type: array + items: + $ref: '#/components/schemas/CompatibilityResult' + suggestions: + type: array + items: + type: string + createdAt: + type: string + format: date-time + + ValueEvaluationRequest: + type: object + required: + - datasetId + - valueCriteria + properties: + datasetId: + type: string + valueCriteria: + type: array + items: + $ref: '#/components/schemas/ValueCriterion' + marketContext: + type: object + description: 市场环境信息 + businessContext: + type: object + description: 业务环境信息 + + ValueEvaluationResponse: + type: object + properties: + evaluationId: + type: string + valueScore: + type: number + format: double + monetaryValue: + type: number + format: double + description: 货币价值估算 + strategicValue: + type: number + format: double + description: 战略价值评分 + results: + type: array + items: + $ref: '#/components/schemas/ValueResult' + insights: + type: array + items: + type: string + + EvaluationReportResponse: + type: object + properties: + id: + type: string + datasetId: + type: string + type: + $ref: '#/components/schemas/EvaluationType' + status: + $ref: '#/components/schemas/EvaluationStatus' + overallScore: + type: number + format: double + summary: + type: string + createdAt: + type: string + format: date-time + completedAt: + type: string + format: date-time + + EvaluationReportPageResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/EvaluationReportResponse' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + size: + type: integer + number: + type: integer + + EvaluationReportDetailResponse: + allOf: + - $ref: '#/components/schemas/EvaluationReportResponse' + - type: object + properties: + qualityResults: + $ref: '#/components/schemas/QualityEvaluationResponse' + compatibilityResults: + $ref: '#/components/schemas/CompatibilityEvaluationResponse' + valueResults: + $ref: '#/components/schemas/ValueEvaluationResponse' + attachments: + type: array + items: + $ref: '#/components/schemas/ReportAttachment' + + BatchEvaluationRequest: + type: object + required: + - datasetIds + - evaluationTypes + properties: + datasetIds: + type: array + items: + type: string + evaluationTypes: + type: array + items: + $ref: '#/components/schemas/EvaluationType' + parameters: + type: object + + BatchEvaluationResponse: + type: object + properties: + batchId: + type: string + status: + type: string + totalTasks: + type: integer + submittedAt: + type: string + format: date-time + + QualityMetric: + type: string + enum: + - COMPLETENESS + - ACCURACY + - CONSISTENCY + - VALIDITY + - UNIQUENESS + - TIMELINESS + + QualityMetricResult: + type: object + properties: + metric: + $ref: '#/components/schemas/QualityMetric' + score: + type: number + format: double + details: + type: object + issues: + type: array + items: + $ref: '#/components/schemas/QualityIssue' + + DetailedQualityResults: + type: object + properties: + fieldAnalysis: + type: array + items: + $ref: '#/components/schemas/FieldAnalysis' + distributionAnalysis: + $ref: '#/components/schemas/DistributionAnalysis' + correlationAnalysis: + $ref: '#/components/schemas/CorrelationAnalysis' + + TargetType: + type: string + enum: + - LANGUAGE_MODEL + - CLASSIFICATION_MODEL + - RECOMMENDATION_SYSTEM + - CUSTOM_TASK + + CompatibilityCriterion: + type: string + enum: + - FORMAT_COMPATIBILITY + - SCHEMA_COMPATIBILITY + - SIZE_ADEQUACY + - DISTRIBUTION_MATCH + - FEATURE_COVERAGE + + CompatibilityResult: + type: object + properties: + criterion: + $ref: '#/components/schemas/CompatibilityCriterion' + score: + type: number + format: double + status: + type: string + enum: [PASS, WARN, FAIL] + details: + type: string + + ValueCriterion: + type: string + enum: + - RARITY + - DEMAND + - QUALITY + - COMPLETENESS + - TIMELINESS + - STRATEGIC_IMPORTANCE + + ValueResult: + type: object + properties: + criterion: + $ref: '#/components/schemas/ValueCriterion' + score: + type: number + format: double + impact: + type: string + enum: [LOW, MEDIUM, HIGH] + explanation: + type: string + + EvaluationType: + type: string + enum: + - QUALITY + - COMPATIBILITY + - VALUE + - COMPREHENSIVE + + EvaluationStatus: + type: string + enum: + - PENDING + - RUNNING + - COMPLETED + - FAILED + + QualityIssue: + type: object + properties: + type: + type: string + severity: + type: string + enum: [LOW, MEDIUM, HIGH, CRITICAL] + description: + type: string + affectedRecords: + type: integer + suggestions: + type: array + items: + type: string + + FieldAnalysis: + type: object + properties: + fieldName: + type: string + dataType: + type: string + nullCount: + type: integer + uniqueCount: + type: integer + statistics: + type: object + + DistributionAnalysis: + type: object + properties: + distributions: + type: array + items: + type: object + outliers: + type: array + items: + type: object + patterns: + type: array + items: + type: string + + CorrelationAnalysis: + type: object + properties: + correlationMatrix: + type: array + items: + type: array + items: + type: number + significantCorrelations: + type: array + items: + type: object + + VisualizationData: + type: object + properties: + type: + type: string + enum: [CHART, GRAPH, HISTOGRAM, HEATMAP] + title: + type: string + data: + type: object + config: + type: object + + ReportAttachment: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + size: + type: integer + format: int64 + downloadUrl: + type: string + + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + +security: + - BearerAuth: [] diff --git a/backend/openapi/specs/data-management.yaml b/backend/openapi/specs/data-management.yaml new file mode 100644 index 0000000..d77083b --- /dev/null +++ b/backend/openapi/specs/data-management.yaml @@ -0,0 +1,719 @@ +openapi: 3.0.3 +info: + title: Data Management Service API + description: | + 数据管理服务API,提供数据集的创建、管理和文件操作功能。 + + 主要功能: + - 数据集的创建和管理 + - 多种数据集类型支持(图像、文本、音频、视频、多模态等) + - 数据集文件管理 + - 数据集标签和元数据管理 + - 数据集统计信息 + version: 1.0.0 + +servers: + - url: http://localhost:8092/api/v1/data-management + description: Development server + +tags: + - name: Dataset + description: 数据集管理 + - name: DatasetFile + description: 数据集文件管理 + - name: DatasetType + description: 数据集类型管理 + - name: Tag + description: 标签管理 + +paths: + /data-management/datasets: + get: + tags: [Dataset] + operationId: getDatasets + summary: 获取数据集列表 + description: 分页查询数据集列表,支持按类型、标签等条件筛选 + parameters: + - name: page + in: query + schema: + type: integer + default: 0 + description: 页码,从0开始 + - name: size + in: query + schema: + type: integer + default: 20 + description: 每页大小 + - name: type + in: query + schema: + type: string + description: 数据集类型过滤 + - name: tags + in: query + schema: + type: string + description: 标签过滤,多个标签用逗号分隔 + - name: keyword + in: query + schema: + type: string + description: 关键词搜索(名称、描述) + - name: status + in: query + schema: + type: string + enum: [ACTIVE, INACTIVE, PROCESSING] + description: 数据集状态过滤 + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDatasetResponse' + '400': + description: 请求参数错误 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + post: + tags: [Dataset] + operationId: createDataset + summary: 创建数据集 + description: 创建新的数据集 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateDatasetRequest' + responses: + '201': + description: 创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/DatasetResponse' + '400': + description: 请求参数错误 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /data-management/datasets/{datasetId}: + get: + tags: [Dataset] + operationId: getDatasetById + summary: 获取数据集详情 + description: 根据ID获取数据集详细信息 + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/DatasetResponse' + '404': + description: 数据集不存在 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + put: + tags: [Dataset] + summary: 更新数据集 + operationId: updateDataset + description: 更新数据集信息 + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateDatasetRequest' + responses: + '200': + description: 更新成功 + content: + application/json: + schema: + $ref: '#/components/schemas/DatasetResponse' + '404': + description: 数据集不存在 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + delete: + tags: [Dataset] + operationId: deleteDataset + summary: 删除数据集 + description: 删除指定的数据集 + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + responses: + '204': + description: 删除成功 + '404': + description: 数据集不存在 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /data-management/datasets/{datasetId}/files: + get: + tags: [DatasetFile] + summary: 获取数据集文件列表 + operationId: getDatasetFiles + description: 分页获取数据集中的文件列表 + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + - name: page + in: query + schema: + type: integer + default: 0 + description: 页码,从0开始 + - name: size + in: query + schema: + type: integer + default: 20 + description: 每页大小 + - name: fileType + in: query + schema: + type: string + description: 文件类型过滤 + - name: status + in: query + schema: + type: string + enum: [UPLOADED, PROCESSING, COMPLETED, ERROR] + description: 文件状态过滤 + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDatasetFileResponse' + + post: + tags: [DatasetFile] + summary: 上传文件到数据集 + operationId: uploadDatasetFile + description: 向指定数据集上传文件 + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + description: 要上传的文件 + description: + type: string + description: 文件描述 + responses: + '201': + description: 上传成功 + content: + application/json: + schema: + $ref: '#/components/schemas/DatasetFileResponse' + + /data-management/datasets/{datasetId}/files/{fileId}: + get: + tags: [DatasetFile] + summary: 获取文件详情 + description: 获取数据集中指定文件的详细信息 + operationId: getDatasetFileById + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + - name: fileId + in: path + required: true + schema: + type: string + description: 文件ID + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/DatasetFileResponse' + + delete: + tags: [DatasetFile] + summary: 删除文件 + operationId: deleteDatasetFile + description: 从数据集中删除指定文件 + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + - name: fileId + in: path + required: true + schema: + type: string + description: 文件ID + responses: + '204': + description: 删除成功 + + /data-management/datasets/{datasetId}/files/{fileId}/download: + get: + tags: [DatasetFile] + operationId: downloadDatasetFile + summary: 下载文件 + description: 下载数据集中的指定文件 + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + - name: fileId + in: path + required: true + schema: + type: string + description: 文件ID + responses: + '200': + description: 文件内容 + content: + application/octet-stream: + schema: + type: string + format: binary + + /data-management/dataset-types: + get: + operationId: getDatasetTypes + tags: [DatasetType] + summary: 获取数据集类型列表 + description: 获取所有支持的数据集类型 + responses: + '200': + description: 成功 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DatasetTypeResponse' + + /data-management/tags: + get: + tags: [Tag] + operationId: getTags + summary: 获取标签列表 + description: 获取所有可用的标签 + parameters: + - name: keyword + in: query + schema: + type: string + description: 标签名称关键词搜索 + responses: + '200': + description: 成功 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TagResponse' + + post: + tags: [Tag] + operationId: createTag + summary: 创建标签 + description: 创建新的标签 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateTagRequest' + responses: + '201': + description: 创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/TagResponse' + + /data-management/datasets/{datasetId}/statistics: + get: + tags: [Dataset] + operationId: getDatasetStatistics + summary: 获取数据集统计信息 + description: 获取数据集的统计信息(文件数量、大小、完成度等) + parameters: + - name: datasetId + in: path + required: true + schema: + type: string + description: 数据集ID + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/DatasetStatisticsResponse' + +components: + schemas: + PagedDatasetResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/DatasetResponse' + page: + type: integer + description: 当前页码 + size: + type: integer + description: 每页大小 + totalElements: + type: integer + description: 总元素数 + totalPages: + type: integer + description: 总页数 + first: + type: boolean + description: 是否为第一页 + last: + type: boolean + description: 是否为最后一页 + + DatasetResponse: + type: object + properties: + id: + type: string + description: 数据集ID + name: + type: string + description: 数据集名称 + description: + type: string + description: 数据集描述 + type: + $ref: '#/components/schemas/DatasetTypeResponse' + status: + type: string + enum: [ACTIVE, INACTIVE, PROCESSING] + description: 数据集状态 + tags: + type: array + items: + $ref: '#/components/schemas/TagResponse' + description: 标签列表 + dataSource: + type: string + description: 数据源 + targetLocation: + type: string + description: 目标位置 + fileCount: + type: integer + description: 文件数量 + totalSize: + type: integer + format: int64 + description: 总大小(字节) + completionRate: + type: number + format: float + description: 完成率(0-100) + createdAt: + type: string + format: date-time + description: 创建时间 + updatedAt: + type: string + format: date-time + description: 更新时间 + createdBy: + type: string + description: 创建者 + + CreateDatasetRequest: + type: object + required: + - name + - type + properties: + name: + type: string + description: 数据集名称 + minLength: 1 + maxLength: 100 + description: + type: string + description: 数据集描述 + maxLength: 500 + type: + type: string + description: 数据集类型 + tags: + type: array + items: + type: string + description: 标签列表 + dataSource: + type: string + description: 数据源 + targetLocation: + type: string + description: 目标位置 + + UpdateDatasetRequest: + type: object + properties: + name: + type: string + description: 数据集名称 + maxLength: 100 + description: + type: string + description: 数据集描述 + maxLength: 500 + tags: + type: array + items: + type: string + description: 标签列表 + status: + type: string + enum: [ACTIVE, INACTIVE] + description: 数据集状态 + + DatasetTypeResponse: + type: object + properties: + code: + type: string + description: 类型编码 + name: + type: string + description: 类型名称 + description: + type: string + description: 类型描述 + supportedFormats: + type: array + items: + type: string + description: 支持的文件格式 + icon: + type: string + description: 图标 + + PagedDatasetFileResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/DatasetFileResponse' + page: + type: integer + description: 当前页码 + size: + type: integer + description: 每页大小 + totalElements: + type: integer + description: 总元素数 + totalPages: + type: integer + description: 总页数 + first: + type: boolean + description: 是否为第一页 + last: + type: boolean + description: 是否为最后一页 + + DatasetFileResponse: + type: object + properties: + id: + type: string + description: 文件ID + fileName: + type: string + description: 文件名 + originalName: + type: string + description: 原始文件名 + fileType: + type: string + description: 文件类型 + fileSize: + type: integer + format: int64 + description: 文件大小(字节) + status: + type: string + enum: [UPLOADED, PROCESSING, COMPLETED, ERROR] + description: 文件状态 + description: + type: string + description: 文件描述 + filePath: + type: string + description: 文件路径 + uploadTime: + type: string + format: date-time + description: 上传时间 + uploadedBy: + type: string + description: 上传者 + + TagResponse: + type: object + properties: + id: + type: string + description: 标签ID + name: + type: string + description: 标签名称 + color: + type: string + description: 标签颜色 + description: + type: string + description: 标签描述 + usageCount: + type: integer + description: 使用次数 + + CreateTagRequest: + type: object + required: + - name + properties: + name: + type: string + description: 标签名称 + minLength: 1 + maxLength: 50 + color: + type: string + description: 标签颜色 + pattern: '^#[0-9A-Fa-f]{6}$' + description: + type: string + description: 标签描述 + maxLength: 200 + + DatasetStatisticsResponse: + type: object + properties: + totalFiles: + type: integer + description: 总文件数 + completedFiles: + type: integer + description: 已完成文件数 + totalSize: + type: integer + format: int64 + description: 总大小(字节) + completionRate: + type: number + format: float + description: 完成率(0-100) + fileTypeDistribution: + type: object + additionalProperties: + type: integer + description: 文件类型分布 + statusDistribution: + type: object + additionalProperties: + type: integer + description: 状态分布 + + ErrorResponse: + type: object + properties: + error: + type: string + description: 错误代码 + message: + type: string + description: 错误消息 + timestamp: + type: string + format: date-time + description: 错误时间 + path: + type: string + description: 请求路径 diff --git a/backend/openapi/specs/data-synthesis.yaml b/backend/openapi/specs/data-synthesis.yaml new file mode 100644 index 0000000..c7de7d3 --- /dev/null +++ b/backend/openapi/specs/data-synthesis.yaml @@ -0,0 +1,620 @@ +openapi: 3.0.3 +info: + title: Data Synthesis Service API + description: 数据合成服务API - 指令、COT蒸馏、多模态合成 + version: 1.0.0 + contact: + name: Data Mate Platform Team + +servers: + - url: http://localhost:8085 + description: Development server + +tags: + - name: synthesis-templates + description: 合成模板管理 + - name: synthesis-jobs + description: 合成任务管理 + - name: instruction-tuning + description: 指令调优 + - name: cot-distillation + description: COT蒸馏 + +paths: + /api/v1/synthesis/templates: + get: + tags: + - synthesis-templates + summary: 获取合成模板列表 + parameters: + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + - name: type + in: query + schema: + $ref: '#/components/schemas/SynthesisType' + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SynthesisTemplatePageResponse' + + post: + tags: + - synthesis-templates + summary: 创建合成模板 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSynthesisTemplateRequest' + responses: + '201': + description: 创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SynthesisTemplateResponse' + + /api/v1/synthesis/templates/{templateId}: + get: + tags: + - synthesis-templates + summary: 获取合成模板详情 + parameters: + - name: templateId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SynthesisTemplateDetailResponse' + + put: + tags: + - synthesis-templates + summary: 更新合成模板 + parameters: + - name: templateId + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateSynthesisTemplateRequest' + responses: + '200': + description: 更新成功 + + /api/v1/synthesis/jobs: + get: + tags: + - synthesis-jobs + summary: 获取合成任务列表 + parameters: + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + - name: status + in: query + schema: + $ref: '#/components/schemas/JobStatus' + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SynthesisJobPageResponse' + + post: + tags: + - synthesis-jobs + summary: 创建合成任务 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSynthesisJobRequest' + responses: + '201': + description: 任务创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SynthesisJobResponse' + + /api/v1/synthesis/jobs/{jobId}: + get: + tags: + - synthesis-jobs + summary: 获取合成任务详情 + parameters: + - name: jobId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SynthesisJobDetailResponse' + + /api/v1/synthesis/jobs/{jobId}/execute: + post: + tags: + - synthesis-jobs + summary: 执行合成任务 + parameters: + - name: jobId + in: path + required: true + schema: + type: string + responses: + '200': + description: 任务开始执行 + content: + application/json: + schema: + $ref: '#/components/schemas/JobExecutionResponse' + + /api/v1/synthesis/instruction-tuning: + post: + tags: + - instruction-tuning + summary: 指令调优数据合成 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/InstructionTuningRequest' + responses: + '200': + description: 合成成功 + content: + application/json: + schema: + $ref: '#/components/schemas/InstructionTuningResponse' + + /api/v1/synthesis/cot-distillation: + post: + tags: + - cot-distillation + summary: COT蒸馏数据合成 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/COTDistillationRequest' + responses: + '200': + description: 蒸馏成功 + content: + application/json: + schema: + $ref: '#/components/schemas/COTDistillationResponse' + +components: + schemas: + SynthesisTemplateResponse: + type: object + properties: + id: + type: string + name: + type: string + description: + type: string + type: + $ref: '#/components/schemas/SynthesisType' + category: + type: string + modelConfig: + $ref: '#/components/schemas/ModelConfig' + enabled: + type: boolean + createdAt: + type: string + format: date-time + + SynthesisTemplateDetailResponse: + allOf: + - $ref: '#/components/schemas/SynthesisTemplateResponse' + - type: object + properties: + promptTemplate: + type: string + parameters: + type: object + examples: + type: array + items: + $ref: '#/components/schemas/SynthesisExample' + + SynthesisTemplatePageResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/SynthesisTemplateResponse' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + size: + type: integer + number: + type: integer + + CreateSynthesisTemplateRequest: + type: object + required: + - name + - type + - promptTemplate + properties: + name: + type: string + description: + type: string + type: + $ref: '#/components/schemas/SynthesisType' + category: + type: string + promptTemplate: + type: string + modelConfig: + $ref: '#/components/schemas/ModelConfig' + parameters: + type: object + + UpdateSynthesisTemplateRequest: + type: object + properties: + name: + type: string + description: + type: string + promptTemplate: + type: string + enabled: + type: boolean + parameters: + type: object + + SynthesisJobResponse: + type: object + properties: + id: + type: string + name: + type: string + description: + type: string + templateId: + type: string + status: + $ref: '#/components/schemas/JobStatus' + progress: + type: number + format: double + targetCount: + type: integer + generatedCount: + type: integer + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + createdAt: + type: string + format: date-time + + SynthesisJobDetailResponse: + allOf: + - $ref: '#/components/schemas/SynthesisJobResponse' + - type: object + properties: + template: + $ref: '#/components/schemas/SynthesisTemplateResponse' + statistics: + $ref: '#/components/schemas/SynthesisStatistics' + samples: + type: array + items: + $ref: '#/components/schemas/GeneratedSample' + + SynthesisJobPageResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/SynthesisJobResponse' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + size: + type: integer + number: + type: integer + + CreateSynthesisJobRequest: + type: object + required: + - name + - templateId + - targetCount + properties: + name: + type: string + description: + type: string + templateId: + type: string + targetCount: + type: integer + parameters: + type: object + seedData: + type: array + items: + type: object + + JobExecutionResponse: + type: object + properties: + executionId: + type: string + status: + type: string + message: + type: string + + InstructionTuningRequest: + type: object + required: + - baseInstructions + - targetDomain + - count + properties: + baseInstructions: + type: array + items: + type: string + targetDomain: + type: string + count: + type: integer + modelConfig: + $ref: '#/components/schemas/ModelConfig' + parameters: + type: object + + InstructionTuningResponse: + type: object + properties: + jobId: + type: string + generatedInstructions: + type: array + items: + $ref: '#/components/schemas/GeneratedInstruction' + statistics: + $ref: '#/components/schemas/GenerationStatistics' + + COTDistillationRequest: + type: object + required: + - sourceModel + - targetFormat + - examples + properties: + sourceModel: + type: string + targetFormat: + type: string + enum: [QA, INSTRUCTION, REASONING] + examples: + type: array + items: + $ref: '#/components/schemas/COTExample' + parameters: + type: object + + COTDistillationResponse: + type: object + properties: + jobId: + type: string + distilledData: + type: array + items: + $ref: '#/components/schemas/DistilledCOTData' + statistics: + $ref: '#/components/schemas/DistillationStatistics' + + SynthesisType: + type: string + enum: + - INSTRUCTION_TUNING + - COT_DISTILLATION + - DIALOGUE_GENERATION + - TEXT_AUGMENTATION + - MULTIMODAL_SYNTHESIS + - CUSTOM + + JobStatus: + type: string + enum: + - PENDING + - RUNNING + - COMPLETED + - FAILED + - CANCELLED + + ModelConfig: + type: object + properties: + modelName: + type: string + temperature: + type: number + format: double + maxTokens: + type: integer + topP: + type: number + format: double + frequencyPenalty: + type: number + format: double + + SynthesisExample: + type: object + properties: + input: + type: string + output: + type: string + explanation: + type: string + + SynthesisStatistics: + type: object + properties: + totalGenerated: + type: integer + successfulGenerated: + type: integer + failedGenerated: + type: integer + averageLength: + type: number + format: double + uniqueCount: + type: integer + + GeneratedSample: + type: object + properties: + id: + type: string + content: + type: string + score: + type: number + format: double + metadata: + type: object + createdAt: + type: string + format: date-time + + GeneratedInstruction: + type: object + properties: + instruction: + type: string + input: + type: string + output: + type: string + quality: + type: number + format: double + + GenerationStatistics: + type: object + properties: + totalGenerated: + type: integer + averageQuality: + type: number + format: double + diversityScore: + type: number + format: double + + COTExample: + type: object + properties: + question: + type: string + reasoning: + type: string + answer: + type: string + + DistilledCOTData: + type: object + properties: + question: + type: string + reasoning: + type: string + answer: + type: string + confidence: + type: number + format: double + + DistillationStatistics: + type: object + properties: + totalProcessed: + type: integer + successfulDistilled: + type: integer + averageConfidence: + type: number + format: double + + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + +security: + - BearerAuth: [] diff --git a/backend/openapi/specs/execution-engine.yaml b/backend/openapi/specs/execution-engine.yaml new file mode 100644 index 0000000..276782a --- /dev/null +++ b/backend/openapi/specs/execution-engine.yaml @@ -0,0 +1,712 @@ +openapi: 3.0.3 +info: + title: Execution Engine Service API + description: 执行引擎服务API - 与Ray/DataX/Python执行器对接 + version: 1.0.0 + contact: + name: Data Mate Platform Team + +servers: + - url: http://localhost:8088 + description: Development server + +tags: + - name: jobs + description: 作业管理 + - name: executors + description: 执行器管理 + - name: resources + description: 资源管理 + - name: monitoring + description: 监控管理 + +paths: + /api/v1/jobs: + get: + tags: + - jobs + summary: 获取作业列表 + parameters: + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + - name: status + in: query + schema: + $ref: '#/components/schemas/JobStatus' + - name: executor + in: query + schema: + $ref: '#/components/schemas/ExecutorType' + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/JobPageResponse' + + post: + tags: + - jobs + summary: 提交作业 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitJobRequest' + responses: + '201': + description: 作业提交成功 + content: + application/json: + schema: + $ref: '#/components/schemas/JobResponse' + + /api/v1/jobs/{jobId}: + get: + tags: + - jobs + summary: 获取作业详情 + parameters: + - name: jobId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/JobDetailResponse' + + delete: + tags: + - jobs + summary: 取消作业 + parameters: + - name: jobId + in: path + required: true + schema: + type: string + responses: + '200': + description: 取消成功 + + /api/v1/jobs/{jobId}/logs: + get: + tags: + - jobs + summary: 获取作业日志 + parameters: + - name: jobId + in: path + required: true + schema: + type: string + - name: follow + in: query + description: 是否实时跟踪日志 + schema: + type: boolean + default: false + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/JobLog' + + /api/v1/jobs/{jobId}/retry: + post: + tags: + - jobs + summary: 重试作业 + parameters: + - name: jobId + in: path + required: true + schema: + type: string + responses: + '200': + description: 重试成功 + content: + application/json: + schema: + $ref: '#/components/schemas/JobResponse' + + /api/v1/executors: + get: + tags: + - executors + summary: 获取执行器列表 + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ExecutorResponse' + + post: + tags: + - executors + summary: 注册执行器 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterExecutorRequest' + responses: + '201': + description: 注册成功 + + /api/v1/executors/{executorId}: + get: + tags: + - executors + summary: 获取执行器详情 + parameters: + - name: executorId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ExecutorDetailResponse' + + put: + tags: + - executors + summary: 更新执行器 + parameters: + - name: executorId + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateExecutorRequest' + responses: + '200': + description: 更新成功 + + /api/v1/resources/clusters: + get: + tags: + - resources + summary: 获取集群信息 + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ClusterInfo' + + /api/v1/resources/nodes: + get: + tags: + - resources + summary: 获取节点信息 + parameters: + - name: clusterId + in: query + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/NodeInfo' + + /api/v1/monitoring/metrics: + get: + tags: + - monitoring + summary: 获取监控指标 + parameters: + - name: metric + in: query + schema: + type: string + - name: start + in: query + schema: + type: string + format: date-time + - name: end + in: query + schema: + type: string + format: date-time + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/MetricsResponse' + +components: + schemas: + JobResponse: + type: object + properties: + id: + type: string + name: + type: string + status: + $ref: '#/components/schemas/JobStatus' + executorType: + $ref: '#/components/schemas/ExecutorType' + priority: + type: integer + progress: + type: number + format: double + submittedAt: + type: string + format: date-time + startedAt: + type: string + format: date-time + completedAt: + type: string + format: date-time + submittedBy: + type: string + + JobDetailResponse: + allOf: + - $ref: '#/components/schemas/JobResponse' + - type: object + properties: + configuration: + $ref: '#/components/schemas/JobConfiguration' + resources: + $ref: '#/components/schemas/ResourceRequirement' + metrics: + $ref: '#/components/schemas/JobMetrics' + artifacts: + type: array + items: + $ref: '#/components/schemas/JobArtifact' + dependencies: + type: array + items: + type: string + + JobPageResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/JobResponse' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + size: + type: integer + number: + type: integer + + SubmitJobRequest: + type: object + required: + - name + - executorType + - configuration + properties: + name: + type: string + description: + type: string + executorType: + $ref: '#/components/schemas/ExecutorType' + priority: + type: integer + minimum: 1 + maximum: 10 + default: 5 + configuration: + $ref: '#/components/schemas/JobConfiguration' + resources: + $ref: '#/components/schemas/ResourceRequirement' + dependencies: + type: array + items: + type: string + timeoutSeconds: + type: integer + + JobConfiguration: + type: object + properties: + script: + type: string + description: 执行脚本或代码 + arguments: + type: array + items: + type: string + description: 执行参数 + environment: + type: object + description: 环境变量 + files: + type: array + items: + $ref: '#/components/schemas/FileReference' + packages: + type: array + items: + type: string + description: 依赖包列表 + + ResourceRequirement: + type: object + properties: + cpuCores: + type: number + format: double + memoryGB: + type: number + format: double + gpuCount: + type: integer + diskGB: + type: number + format: double + nodeSelector: + type: object + description: 节点选择器 + + ExecutorResponse: + type: object + properties: + id: + type: string + name: + type: string + type: + $ref: '#/components/schemas/ExecutorType' + status: + $ref: '#/components/schemas/ExecutorStatus' + version: + type: string + capabilities: + type: array + items: + type: string + registeredAt: + type: string + format: date-time + lastHeartbeat: + type: string + format: date-time + + ExecutorDetailResponse: + allOf: + - $ref: '#/components/schemas/ExecutorResponse' + - type: object + properties: + configuration: + type: object + resources: + $ref: '#/components/schemas/ExecutorResources' + currentJobs: + type: array + items: + $ref: '#/components/schemas/JobResponse' + statistics: + $ref: '#/components/schemas/ExecutorStatistics' + + RegisterExecutorRequest: + type: object + required: + - name + - type + - endpoint + properties: + name: + type: string + type: + $ref: '#/components/schemas/ExecutorType' + endpoint: + type: string + capabilities: + type: array + items: + type: string + configuration: + type: object + + UpdateExecutorRequest: + type: object + properties: + status: + $ref: '#/components/schemas/ExecutorStatus' + configuration: + type: object + + ClusterInfo: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + enum: [RAY, KUBERNETES, YARN, STANDALONE] + status: + type: string + enum: [ACTIVE, INACTIVE, ERROR] + nodeCount: + type: integer + totalCpuCores: + type: integer + totalMemoryGB: + type: number + format: double + totalGpuCount: + type: integer + availableResources: + $ref: '#/components/schemas/ResourceInfo' + + NodeInfo: + type: object + properties: + id: + type: string + name: + type: string + clusterId: + type: string + status: + type: string + enum: [ACTIVE, INACTIVE, BUSY, ERROR] + resources: + $ref: '#/components/schemas/ResourceInfo' + usage: + $ref: '#/components/schemas/ResourceUsage' + lastUpdate: + type: string + format: date-time + + MetricsResponse: + type: object + properties: + metric: + type: string + dataPoints: + type: array + items: + $ref: '#/components/schemas/MetricDataPoint' + aggregation: + type: object + + JobLog: + type: object + properties: + timestamp: + type: string + format: date-time + level: + type: string + enum: [DEBUG, INFO, WARN, ERROR] + source: + type: string + message: + type: string + + JobMetrics: + type: object + properties: + cpuUsage: + type: number + format: double + memoryUsage: + type: number + format: double + diskUsage: + type: number + format: double + networkIO: + type: object + duration: + type: integer + format: int64 + + JobArtifact: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + enum: [LOG, OUTPUT, CHECKPOINT, MODEL] + size: + type: integer + format: int64 + path: + type: string + createdAt: + type: string + format: date-time + + FileReference: + type: object + properties: + name: + type: string + path: + type: string + type: + type: string + enum: [LOCAL, HDFS, S3, HTTP] + + ExecutorResources: + type: object + properties: + total: + $ref: '#/components/schemas/ResourceInfo' + available: + $ref: '#/components/schemas/ResourceInfo' + allocated: + $ref: '#/components/schemas/ResourceInfo' + + ExecutorStatistics: + type: object + properties: + totalJobs: + type: integer + successfulJobs: + type: integer + failedJobs: + type: integer + averageExecutionTime: + type: number + format: double + uptime: + type: integer + format: int64 + + ResourceInfo: + type: object + properties: + cpuCores: + type: number + format: double + memoryGB: + type: number + format: double + gpuCount: + type: integer + diskGB: + type: number + format: double + + ResourceUsage: + type: object + properties: + cpuUsagePercent: + type: number + format: double + memoryUsagePercent: + type: number + format: double + diskUsagePercent: + type: number + format: double + + MetricDataPoint: + type: object + properties: + timestamp: + type: string + format: date-time + value: + type: number + format: double + tags: + type: object + + JobStatus: + type: string + enum: + - SUBMITTED + - PENDING + - RUNNING + - COMPLETED + - FAILED + - CANCELLED + - TIMEOUT + + ExecutorType: + type: string + enum: + - RAY + - DATAX + - PYTHON + - SPARK + - FLINK + - CUSTOM + + ExecutorStatus: + type: string + enum: + - ACTIVE + - INACTIVE + - BUSY + - ERROR + - MAINTENANCE + + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + +security: + - BearerAuth: [] diff --git a/backend/openapi/specs/operator-market.yaml b/backend/openapi/specs/operator-market.yaml new file mode 100644 index 0000000..8a6c172 --- /dev/null +++ b/backend/openapi/specs/operator-market.yaml @@ -0,0 +1,547 @@ +openapi: 3.0.1 +info: + title: Operator Market Service API + description: | + 算子市场服务API,提供算子的发布、管理和订阅功能。 + + 主要功能: + - 算子发布和管理 + - 算子版本控制 + - 算子评分和评论 + - 算子分类和标签 + - 算子下载和安装 + version: 1.0.0 +tags: + - name: Operator + - name: Category + - name: Label +paths: + /operators/list: + post: + summary: 获取算子列表 + deprecated: false + description: 分页查询算子列表,支持按分类、标签等条件筛选 + tags: + - Operator + parameters: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + page: + type: integer + description: 页数 + size: + type: integer + description: 单页数量 + categories: + type: array + items: + type: integer + description: 分类id列表 + operatorName: + type: string + description: 算子名称 + labelName: + type: string + description: 标签名称 + isStar: + type: boolean + description: 是否收藏 + required: + - page + - size + - categories + examples: {} + responses: + '200': + description: 成功返回算子列表 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/OperatorResponse' + headers: {} + security: [] + /operators/create: + post: + summary: 创建新算子 + deprecated: false + description: 创建并发布一个新的算子 + tags: + - Operator + parameters: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateOperatorRequest' + example: null + responses: + '201': + description: 算子创建成功 + content: + application/json: + schema: &ref_0 + $ref: '#/components/schemas/OperatorResponse' + headers: {} + security: [] + /operators/upload: + post: + summary: 上传新算子 + deprecated: false + description: 创建并发布一个新的算子 + tags: + - Operator + parameters: [] + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + example: '' + description: + type: string + example: '' + examples: {} + responses: + '201': + description: 算子创建成功 + content: + application/json: + schema: *ref_0 + headers: {} + security: [] + /operators/{id}: + get: + summary: 获取算子详情 + deprecated: false + description: 根据ID获取算子的详细信息 + tags: + - Operator + parameters: + - name: id + in: path + description: 算子ID + required: true + example: '' + schema: + type: string + responses: + '200': + description: 成功返回算子详情 + content: + application/json: + schema: *ref_0 + headers: {} + '404': + description: 算子不存在 + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + headers: {} + security: [] + put: + summary: 更新算子信息 + deprecated: false + description: 根据ID更新算子信息 + tags: + - Operator + parameters: + - name: id + in: path + description: 算子ID + required: true + example: '' + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateOperatorRequest' + example: null + responses: + '200': + description: 算子更新成功 + content: + application/json: + schema: *ref_0 + headers: {} + security: [] + /category: + post: + summary: 创建算子分类 + deprecated: false + description: '' + tags: + - Category + parameters: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: 名称 + parentId: + type: integer + description: 父分类id + required: + - name + - parentId + responses: + '201': + description: '' + headers: {} + security: [] + delete: + summary: 删除算子分类 + deprecated: false + description: '' + tags: + - Category + parameters: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + id: + type: integer + description: ID 编号 + required: + - id + responses: + '204': + description: '' + headers: {} + security: [] + /categories/tree: + get: + summary: 获取算子分类列表 + deprecated: false + description: 获取所有可用的算子分类 + tags: + - Category + parameters: [] + responses: + '200': + description: 成功返回分类列表 + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: integer + name: + type: string + count: + type: integer + categories: + $ref: '#/components/schemas/CategoryResponse' + required: + - id + - name + - count + - categories + headers: {} + security: [] + /labels: + get: + summary: 获取算子标签列表 + deprecated: false + description: 获取所有算子的标签 + tags: + - Label + parameters: + - name: page + in: query + description: 页码,从0开始 + required: false + schema: + type: integer + default: 0 + - name: size + in: query + description: 每页大小 + required: false + schema: + type: integer + default: 20 + - name: keyword + in: query + description: 关键词搜索 + required: false + schema: + type: string + responses: + '200': + description: 成功返回标签列表 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/LabelResponse' + headers: {} + security: [] + post: + summary: 创建标签 + deprecated: false + description: 批量创建标签 + tags: + - Label + parameters: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: 名称 + required: + - name + example: veniam + responses: + '201': + description: 创建成功 + headers: {} + security: [] + delete: + summary: 删除标签 + deprecated: false + description: 批量删除标签 + tags: + - Label + parameters: [] + requestBody: + content: + application/json: + schema: + type: array + items: + type: integer + format: int64 + description: 标签id列表 + example: null + responses: + '204': + description: 删除成功 + headers: {} + security: [] + /labels/{id}: + put: + summary: 更新标签 + deprecated: false + description: 更新标签 + tags: + - Label + parameters: + - name: id + in: path + description: 标签ID + required: true + example: '' + schema: + type: string + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UpdateLabelRequest' + example: null + responses: + '200': + description: 更新成功 + headers: {} + security: [] +components: + schemas: + UpdateLabelRequest: + type: object + required: + - id + - name + properties: + id: + type: integer + description: 标签id + name: + type: string + description: 标签名称 + Response: + type: object + properties: + code: + type: string + message: + type: string + data: + type: object + properties: {} + required: + - code + - message + - data + LabelResponse: + type: object + properties: + id: + type: string + description: 标签ID + name: + type: string + description: 标签名称 + SubCategory: + type: object + properties: + id: + type: integer + description: 分类id + name: + type: string + description: 分类名称 + count: + type: integer + type: + type: string + description: 分类类型(0:预置,1:自定义) + parentId: + type: integer + description: 父分类id + required: + - id + - name + - type + - parentId + - count + CategoryResponse: + type: array + items: + $ref: '#/components/schemas/SubCategory' + UpdateOperatorRequest: + type: object + properties: + name: + type: string + description: 算子名称 + description: + type: string + description: 算子描述 + version: + type: string + description: 算子版本 + category: + type: string + description: 算子分类 + documentation: + type: string + description: 文档内容 + ErrorResponse: + type: object + properties: + error: + type: string + description: 错误代码 + message: + type: string + description: 错误信息 + timestamp: + type: string + format: date-time + description: 错误时间 + OperatorResponse: + type: object + properties: + id: + type: string + description: 算子ID + name: + type: string + description: 算子名称 + description: + type: string + description: 算子描述 + version: + type: string + description: 算子版本 + inputs: + type: string + description: 输入类型 + outputs: + type: string + description: 输入类型 + categories: + type: array + description: 算子分类列表 + items: + type: integer + runtime: + type: string + description: 运行时设置 + settings: + type: string + description: 算子参数 + isStar: + type: boolean + description: 是否收藏 + createdAt: + type: string + format: date-time + description: 创建时间 + updatedAt: + type: string + format: date-time + description: 更新时间 + required: + - language + - modal + - inputs + - outputs + - runtime + - settings + - isStar + CreateOperatorRequest: + type: object + required: + - name + - description + - version + - category + properties: + name: + type: string + description: 算子名称 + description: + type: string + description: 算子描述 + version: + type: string + description: 算子版本 + category: + type: string + description: 算子分类 + documentation: + type: string + description: 文档内容 + securitySchemes: {} +servers: [] diff --git a/backend/openapi/specs/pipeline-orchestration.yaml b/backend/openapi/specs/pipeline-orchestration.yaml new file mode 100644 index 0000000..8a2de68 --- /dev/null +++ b/backend/openapi/specs/pipeline-orchestration.yaml @@ -0,0 +1,639 @@ +openapi: 3.0.3 +info: + title: Pipeline Orchestration Service API + description: 流程编排服务API - 可视化、模板、执行计划 + version: 1.0.0 + contact: + name: Data Mate Platform Team + +servers: + - url: http://localhost:8087 + description: Development server + +tags: + - name: pipelines + description: 流水线管理 + - name: pipeline-templates + description: 流水线模板 + - name: executions + description: 执行管理 + - name: workflows + description: 工作流编排 + +paths: + /api/v1/pipelines: + get: + tags: + - pipelines + summary: 获取流水线列表 + parameters: + - name: page + in: query + schema: + type: integer + default: 0 + - name: size + in: query + schema: + type: integer + default: 20 + - name: status + in: query + schema: + $ref: '#/components/schemas/PipelineStatus' + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/PipelinePageResponse' + + post: + tags: + - pipelines + summary: 创建流水线 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePipelineRequest' + responses: + '201': + description: 创建成功 + content: + application/json: + schema: + $ref: '#/components/schemas/PipelineResponse' + + /api/v1/pipelines/{pipelineId}: + get: + tags: + - pipelines + summary: 获取流水线详情 + parameters: + - name: pipelineId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/PipelineDetailResponse' + + put: + tags: + - pipelines + summary: 更新流水线 + parameters: + - name: pipelineId + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdatePipelineRequest' + responses: + '200': + description: 更新成功 + + /api/v1/pipelines/{pipelineId}/execute: + post: + tags: + - executions + summary: 执行流水线 + parameters: + - name: pipelineId + in: path + required: true + schema: + type: string + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/ExecutePipelineRequest' + responses: + '200': + description: 执行开始 + content: + application/json: + schema: + $ref: '#/components/schemas/PipelineExecutionResponse' + + /api/v1/executions: + get: + tags: + - executions + summary: 获取执行历史 + parameters: + - name: pipelineId + in: query + schema: + type: string + - name: status + in: query + schema: + $ref: '#/components/schemas/ExecutionStatus' + - name: page + in: query + schema: + type: integer + default: 0 + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ExecutionPageResponse' + + /api/v1/executions/{executionId}: + get: + tags: + - executions + summary: 获取执行详情 + parameters: + - name: executionId + in: path + required: true + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + $ref: '#/components/schemas/ExecutionDetailResponse' + + /api/v1/executions/{executionId}/stop: + post: + tags: + - executions + summary: 停止执行 + parameters: + - name: executionId + in: path + required: true + schema: + type: string + responses: + '200': + description: 停止成功 + + /api/v1/templates: + get: + tags: + - pipeline-templates + summary: 获取模板列表 + parameters: + - name: category + in: query + schema: + type: string + responses: + '200': + description: 获取成功 + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PipelineTemplateResponse' + + post: + tags: + - pipeline-templates + summary: 创建模板 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePipelineTemplateRequest' + responses: + '201': + description: 创建成功 + +components: + schemas: + PipelineResponse: + type: object + properties: + id: + type: string + name: + type: string + description: + type: string + status: + $ref: '#/components/schemas/PipelineStatus' + version: + type: string + category: + type: string + tags: + type: array + items: + type: string + createdBy: + type: string + createdAt: + type: string + format: date-time + lastModified: + type: string + format: date-time + + PipelineDetailResponse: + allOf: + - $ref: '#/components/schemas/PipelineResponse' + - type: object + properties: + definition: + $ref: '#/components/schemas/PipelineDefinition' + parameters: + type: array + items: + $ref: '#/components/schemas/PipelineParameter' + dependencies: + type: array + items: + type: string + statistics: + $ref: '#/components/schemas/PipelineStatistics' + + PipelinePageResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/PipelineResponse' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + size: + type: integer + number: + type: integer + + CreatePipelineRequest: + type: object + required: + - name + - definition + properties: + name: + type: string + description: + type: string + category: + type: string + definition: + $ref: '#/components/schemas/PipelineDefinition' + parameters: + type: array + items: + $ref: '#/components/schemas/PipelineParameter' + tags: + type: array + items: + type: string + + UpdatePipelineRequest: + type: object + properties: + name: + type: string + description: + type: string + definition: + $ref: '#/components/schemas/PipelineDefinition' + status: + $ref: '#/components/schemas/PipelineStatus' + + ExecutePipelineRequest: + type: object + properties: + parameters: + type: object + description: 执行参数 + environment: + type: string + description: 执行环境 + priority: + type: integer + description: 优先级 + + PipelineExecutionResponse: + type: object + properties: + executionId: + type: string + pipelineId: + type: string + status: + $ref: '#/components/schemas/ExecutionStatus' + startTime: + type: string + format: date-time + message: + type: string + + ExecutionResponse: + type: object + properties: + id: + type: string + pipelineId: + type: string + pipelineName: + type: string + status: + $ref: '#/components/schemas/ExecutionStatus' + progress: + type: number + format: double + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + duration: + type: integer + format: int64 + description: 执行时长(毫秒) + + ExecutionDetailResponse: + allOf: + - $ref: '#/components/schemas/ExecutionResponse' + - type: object + properties: + steps: + type: array + items: + $ref: '#/components/schemas/ExecutionStep' + logs: + type: array + items: + $ref: '#/components/schemas/ExecutionLog' + metrics: + $ref: '#/components/schemas/ExecutionMetrics' + artifacts: + type: array + items: + $ref: '#/components/schemas/ExecutionArtifact' + + ExecutionPageResponse: + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/ExecutionResponse' + totalElements: + type: integer + format: int64 + totalPages: + type: integer + size: + type: integer + number: + type: integer + + PipelineTemplateResponse: + type: object + properties: + id: + type: string + name: + type: string + description: + type: string + category: + type: string + version: + type: string + definition: + $ref: '#/components/schemas/PipelineDefinition' + usageCount: + type: integer + createdAt: + type: string + format: date-time + + CreatePipelineTemplateRequest: + type: object + required: + - name + - definition + properties: + name: + type: string + description: + type: string + category: + type: string + definition: + $ref: '#/components/schemas/PipelineDefinition' + + PipelineDefinition: + type: object + properties: + nodes: + type: array + items: + $ref: '#/components/schemas/PipelineNode' + edges: + type: array + items: + $ref: '#/components/schemas/PipelineEdge' + settings: + type: object + description: 流水线设置 + + PipelineNode: + type: object + properties: + id: + type: string + type: + type: string + enum: [OPERATOR, CONDITION, LOOP, PARALLEL] + name: + type: string + operatorId: + type: string + configuration: + type: object + position: + $ref: '#/components/schemas/NodePosition' + + PipelineEdge: + type: object + properties: + id: + type: string + source: + type: string + target: + type: string + condition: + type: string + type: + type: string + enum: [SUCCESS, FAILURE, ALWAYS] + + NodePosition: + type: object + properties: + x: + type: number + y: + type: number + + PipelineParameter: + type: object + properties: + name: + type: string + type: + type: string + required: + type: boolean + defaultValue: + type: string + description: + type: string + + PipelineStatistics: + type: object + properties: + totalExecutions: + type: integer + successfulExecutions: + type: integer + failedExecutions: + type: integer + averageDuration: + type: number + format: double + lastExecutionTime: + type: string + format: date-time + + ExecutionStep: + type: object + properties: + id: + type: string + nodeId: + type: string + name: + type: string + status: + $ref: '#/components/schemas/ExecutionStatus' + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + duration: + type: integer + format: int64 + message: + type: string + + ExecutionLog: + type: object + properties: + timestamp: + type: string + format: date-time + level: + type: string + enum: [DEBUG, INFO, WARN, ERROR] + nodeId: + type: string + message: + type: string + + ExecutionMetrics: + type: object + properties: + totalNodes: + type: integer + completedNodes: + type: integer + failedNodes: + type: integer + cpuUsage: + type: number + format: double + memoryUsage: + type: number + format: double + throughput: + type: number + format: double + + ExecutionArtifact: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + size: + type: integer + format: int64 + path: + type: string + createdAt: + type: string + format: date-time + + PipelineStatus: + type: string + enum: + - DRAFT + - ACTIVE + - INACTIVE + - DEPRECATED + + ExecutionStatus: + type: string + enum: + - PENDING + - RUNNING + - SUCCESS + - FAILED + - CANCELLED + - SKIPPED + + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + +security: + - BearerAuth: [] diff --git a/backend/pom.xml b/backend/pom.xml new file mode 100644 index 0000000..682eaa0 --- /dev/null +++ b/backend/pom.xml @@ -0,0 +1,212 @@ + + + 4.0.0 + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + pom + + DataMatePlatform + 一站式数据工作平台,面向模型微调与RAG检索 + + + 21 + 21 + UTF-8 + + 3.5.6 + 2025.0.0 + 8.0.33 + 42.6.0 + 3.2.0 + 8.11.0 + 5.10.0 + 2.2.0 + 0.2.6 + 3.0.2 + 3.1.0 + 3.3.0 + 3.5.14 + 1.6.3 + 1.18.32 + 0.2.0 + 5.4.0 + 2.21.1 + + + + + shared/domain-common + shared/security-common + + + services/data-management-service + services/data-collection-service + services/operator-market-service + services/data-cleaning-service + services/data-synthesis-service + services/data-annotation-service + services/data-evaluation-service + services/pipeline-orchestration-service + services/execution-engine-service + + + services/rag-indexer-service + services/rag-query-service + + + services/main-application + + + api-gateway + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + org.openapitools + jackson-databind-nullable + ${jackson-databind-nullable.version} + + + jakarta.validation + jakarta.validation-api + ${jakarta-validation.version} + + + + jakarta.persistence + jakarta.persistence-api + ${jakarta.persistence.version} + + + + com.baomidou + mybatis-plus-bom + ${mybatis-plus.version} + pom + import + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + mysql + mysql-connector-java + ${mysql.version} + + + + org.apache.poi + poi + ${poi.version} + + + + + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-logging + + + + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-logging + + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + com.baomidou + mybatis-plus-jsqlparser + + + + + org.springframework.boot + spring-boot-starter-log4j2 + ${spring-boot.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + org.apache.poi + poi + ${poi.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + diff --git a/backend/services/data-annotation-service/pom.xml b/backend/services/data-annotation-service/pom.xml new file mode 100644 index 0000000..a91d058 --- /dev/null +++ b/backend/services/data-annotation-service/pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + data-annotation-service + Data Annotation Service + 数据标注服务 + + + + com.datamate + domain-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-websocket + + + mysql + mysql-connector-java + ${mysql.version} + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.0.4 + + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + jakarta.validation + jakarta.validation-api + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.openapitools + openapi-generator-maven-plugin + 6.6.0 + + + + generate + + + ${project.basedir}/../../openapi/specs/data-annotation.yaml + spring + ${project.build.directory}/generated-sources/openapi + com.datamate.annotation.interfaces.api + com.datamate.annotation.interfaces.dto + + true + true + true + true + true + java8 + true + true + true + springdoc + + + + + + + + + diff --git a/backend/services/data-cleaning-service/img.png b/backend/services/data-cleaning-service/img.png new file mode 100644 index 0000000000000000000000000000000000000000..cee23b96cb639020b9aaa339e5a4e2212622df8d GIT binary patch literal 137462 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 l@e|NlA=!YXUH#k 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

pliz?y}E;+eA1e!C_N=(ARQYK#X*Q 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 CzdWJ< literal 0 HcmV?d00001 diff --git a/backend/services/data-cleaning-service/img2.png b/backend/services/data-cleaning-service/img2.png new file mode 100644 index 0000000000000000000000000000000000000000..1f8aeb11bf4d29a5df0e8eaf09d453319b206886 GIT binary patch literal 92852 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 ltEAv2Wnzi + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + data-cleaning-service + Data Cleaning Service + 数据清洗服务 + + + + com.datamate + domain-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.projectlombok + lombok + + + org.openapitools + jackson-databind-nullable + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + mysql + mysql-connector-java + + + org.apache.commons + commons-compress + 1.26.1 + + + + org.mapstruct + mapstruct + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + org.springframework.data + spring-data-commons + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/DataCleaningServiceConfiguration.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/DataCleaningServiceConfiguration.java new file mode 100644 index 0000000..1825750 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/DataCleaningServiceConfiguration.java @@ -0,0 +1,22 @@ +package com.datamate.cleaning; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 数据归集服务配置类 + * + * 基于DataX的数据归集和同步服务,支持多种数据源的数据采集和归集 + */ +@SpringBootApplication +@EnableAsync +@EnableScheduling +@ComponentScan(basePackages = { + "com.datamate.cleaning", + "com.datamate.shared" +}) +public class DataCleaningServiceConfiguration { + // Configuration class for JAR packaging - no main method needed +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/DatasetClient.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/DatasetClient.java new file mode 100644 index 0000000..f2d6809 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/DatasetClient.java @@ -0,0 +1,120 @@ +package com.datamate.cleaning.application.httpclient; + +import com.datamate.cleaning.domain.model.CreateDatasetRequest; +import com.datamate.cleaning.domain.model.DatasetResponse; +import com.datamate.cleaning.domain.model.PagedDatasetFileResponse; +import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.ErrorCodeImpl; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.text.MessageFormat; +import java.time.Duration; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +public class DatasetClient { + private static final String BASE_URL = "http://localhost:8080/api"; + + private static final String CREATE_DATASET_URL = BASE_URL + "/data-management/datasets"; + + private static final String GET_DATASET_URL = BASE_URL + "/data-management/datasets/{0}"; + + private static final String GET_DATASET_FILE_URL = BASE_URL + "/data-management/datasets/{0}/files"; + + private static final HttpClient CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + static { + OBJECT_MAPPER.registerModule(new JavaTimeModule()); + } + + public static DatasetResponse createDataset(String name, String type) { + CreateDatasetRequest createDatasetRequest = new CreateDatasetRequest(); + createDatasetRequest.setName(name); + createDatasetRequest.setDatasetType(type); + + String jsonPayload; + try { + jsonPayload = OBJECT_MAPPER.writeValueAsString(createDatasetRequest); + } catch (IOException e) { + log.error("Error occurred while converting the object.", e); + throw BusinessException.of(SystemErrorCode.UNKNOWN_ERROR); + } + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(CREATE_DATASET_URL)) + .timeout(Duration.ofSeconds(30)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) + .build(); + + return sendAndReturn(request, DatasetResponse.class); + } + + public static DatasetResponse getDataset(String datasetId) { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(MessageFormat.format(GET_DATASET_URL, datasetId))) + .timeout(Duration.ofSeconds(30)) + .header("Content-Type", "application/json") + .GET() + .build(); + + return sendAndReturn(request, DatasetResponse.class); + } + + public static PagedDatasetFileResponse getDatasetFile(String datasetId, PageRequest page) { + String url = buildQueryParams(MessageFormat.format(GET_DATASET_FILE_URL, datasetId), + Map.of("page", page.getPageNumber(), "size", page.getPageSize())); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .timeout(Duration.ofSeconds(30)) + .header("Content-Type", "application/json") + .GET() + .build(); + + return sendAndReturn(request, PagedDatasetFileResponse.class); + } + + private static T sendAndReturn(HttpRequest request, Class clazz) { + try { + HttpResponse response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); + int statusCode = response.statusCode(); + String responseBody = response.body(); + JsonNode jsonNode = OBJECT_MAPPER.readTree(responseBody); + + if (statusCode < 200 || statusCode >= 300) { + String code = jsonNode.get("code").asText(); + String message = jsonNode.get("message").asText(); + throw BusinessException.of(ErrorCodeImpl.of(code, message)); + } + return OBJECT_MAPPER.treeToValue(jsonNode.get("data"), clazz); + } catch (IOException | InterruptedException e) { + log.error("Error occurred while making the request.", e); + throw BusinessException.of(SystemErrorCode.UNKNOWN_ERROR); + } + } + + private static String buildQueryParams(String baseUrl, Map params) { + if (params == null || params.isEmpty()) { + return baseUrl; + } + + String queryString = params.entrySet().stream() + .map(entry -> entry.getKey() + entry.getValue().toString()) + .collect(Collectors.joining("&")); + + return baseUrl + (baseUrl.contains("?") ? "&" : "?") + queryString; + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/RuntimeClient.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/RuntimeClient.java new file mode 100644 index 0000000..6c47cbf --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/httpclient/RuntimeClient.java @@ -0,0 +1,54 @@ +package com.datamate.cleaning.application.httpclient; + +import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.text.MessageFormat; +import java.time.Duration; + +@Slf4j +public class RuntimeClient { + private static final String BASE_URL = "http://runtime:8081/api"; + + private static final String CREATE_TASK_URL = BASE_URL + "/task/{0}/submit"; + + private static final String STOP_TASK_URL = BASE_URL + "/task/{0}/stop"; + + private static final HttpClient CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); + + public static void submitTask(String taskId) { + send(MessageFormat.format(CREATE_TASK_URL, taskId)); + } + + public static void stopTask(String taskId) { + send(MessageFormat.format(STOP_TASK_URL, taskId)); + } + + private static void send(String url) { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .timeout(Duration.ofSeconds(30)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + + try { + HttpResponse response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); + int statusCode = response.statusCode(); + + if (statusCode < 200 || statusCode >= 300) { + log.error("Request failed with status code: {}", statusCode); + throw BusinessException.of(SystemErrorCode.SYSTEM_BUSY); + } + } catch (IOException | InterruptedException e) { + log.error("Error occurred while making the request.", e); + throw BusinessException.of(SystemErrorCode.UNKNOWN_ERROR); + } + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/scheduler/CleaningTaskScheduler.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/scheduler/CleaningTaskScheduler.java new file mode 100644 index 0000000..0bd8a3c --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/scheduler/CleaningTaskScheduler.java @@ -0,0 +1,40 @@ +package com.datamate.cleaning.application.scheduler; + +import com.datamate.cleaning.application.httpclient.RuntimeClient; +import com.datamate.cleaning.infrastructure.persistence.mapper.CleaningTaskMapper; +import com.datamate.cleaning.interfaces.dto.CleaningTask; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Service +@RequiredArgsConstructor +public class CleaningTaskScheduler { + private final CleaningTaskMapper cleaningTaskMapper; + + private final ExecutorService taskExecutor = Executors.newFixedThreadPool(5); + + public void executeTask(String taskId) { + taskExecutor.submit(() -> submitTask(taskId)); + } + + private void submitTask(String taskId) { + CleaningTask task = new CleaningTask(); + task.setId(taskId); + task.setStatus(CleaningTask.StatusEnum.RUNNING); + task.setStartedAt(LocalDateTime.now()); + cleaningTaskMapper.updateTask(task); + RuntimeClient.submitTask(taskId); + } + + public void stopTask(String taskId) { + RuntimeClient.stopTask(taskId); + CleaningTask task = new CleaningTask(); + task.setId(taskId); + task.setStatus(CleaningTask.StatusEnum.STOPPED); + cleaningTaskMapper.updateTask(task); + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTaskService.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTaskService.java new file mode 100644 index 0000000..46beab3 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTaskService.java @@ -0,0 +1,186 @@ +package com.datamate.cleaning.application.service; + + +import com.datamate.cleaning.application.httpclient.DatasetClient; +import com.datamate.cleaning.application.scheduler.CleaningTaskScheduler; +import com.datamate.cleaning.domain.converter.OperatorInstanceConverter; +import com.datamate.cleaning.domain.model.DatasetResponse; +import com.datamate.cleaning.domain.model.ExecutorType; +import com.datamate.cleaning.domain.model.OperatorInstancePo; +import com.datamate.cleaning.domain.model.PagedDatasetFileResponse; +import com.datamate.cleaning.domain.model.TaskProcess; +import com.datamate.cleaning.infrastructure.persistence.mapper.CleaningResultMapper; +import com.datamate.cleaning.infrastructure.persistence.mapper.CleaningTaskMapper; +import com.datamate.cleaning.infrastructure.persistence.mapper.OperatorInstanceMapper; +import com.datamate.cleaning.interfaces.dto.CleaningTask; +import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest; +import com.datamate.cleaning.interfaces.dto.OperatorInstance; +import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CleaningTaskService { + private final CleaningTaskMapper cleaningTaskMapper; + + private final OperatorInstanceMapper operatorInstanceMapper; + + private final CleaningResultMapper cleaningResultMapper; + + private final CleaningTaskScheduler taskScheduler; + + private final String DATASET_PATH = "/dataset"; + + private final String FLOW_PATH = "/flow"; + + public List getTasks(String status, String keywords, Integer page, Integer size) { + Integer offset = page * size; + return cleaningTaskMapper.findTasks(status, keywords, size, offset); + } + + public int countTasks(String status, String keywords) { + return cleaningTaskMapper.findTasks(status, keywords, null, null).size(); + } + + @Transactional + public CleaningTask createTask(CreateCleaningTaskRequest request) { + DatasetResponse destDataset = DatasetClient.createDataset(request.getDestDatasetName(), + request.getDestDatasetType()); + + DatasetResponse srcDataset = DatasetClient.getDataset(request.getSrcDatasetId()); + + CleaningTask task = new CleaningTask(); + task.setName(request.getName()); + task.setDescription(request.getDescription()); + task.setStatus(CleaningTask.StatusEnum.PENDING); + String taskId = UUID.randomUUID().toString(); + task.setId(taskId); + task.setSrcDatasetId(request.getSrcDatasetId()); + task.setSrcDatasetName(request.getSrcDatasetName()); + task.setDestDatasetId(destDataset.getId()); + task.setDestDatasetName(destDataset.getName()); + task.setBeforeSize(srcDataset.getTotalSize()); + cleaningTaskMapper.insertTask(task); + + List instancePos = request.getInstance().stream() + .map(OperatorInstanceConverter.INSTANCE::operatorToDo).toList(); + operatorInstanceMapper.insertInstance(taskId, instancePos); + + prepareTask(task, request.getInstance()); + scanDataset(taskId, request.getSrcDatasetId()); + executeTask(taskId); + return task; + } + + public CleaningTask getTask(String taskId) { + return cleaningTaskMapper.findTaskById(taskId); + } + + @Transactional + public void deleteTask(String taskId) { + cleaningTaskMapper.deleteTask(taskId); + operatorInstanceMapper.deleteByInstanceId(taskId); + cleaningResultMapper.deleteByInstanceId(taskId); + } + + public void executeTask(String taskId) { + taskScheduler.executeTask(taskId); + } + + private void prepareTask(CleaningTask task, List instances) { + TaskProcess process = new TaskProcess(); + process.setInstanceId(task.getId()); + process.setDatasetId(task.getDestDatasetId()); + process.setDatasetPath(FLOW_PATH + "/" + task.getId() + "/dataset.jsonl"); + process.setExportPath(DATASET_PATH + "/" + task.getDestDatasetId()); + process.setExecutorType(ExecutorType.DATA_PLATFORM.getValue()); + process.setProcess(instances.stream() + .map(instance -> Map.of(instance.getId(), instance.getOverrides())) + .toList()); + + ObjectMapper jsonMapper = new ObjectMapper(new YAMLFactory()); + jsonMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + JsonNode jsonNode = jsonMapper.valueToTree(process); + + DumperOptions options = new DumperOptions(); + options.setIndent(2); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + Yaml yaml = new Yaml(options); + + File file = new File(FLOW_PATH + "/" + process.getInstanceId() + "/process.yaml"); + file.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(file)) { + yaml.dump(jsonMapper.treeToValue(jsonNode, Map.class), writer); + } catch (IOException e) { + log.error("Failed to prepare process.yaml.", e); + throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR); + } + } + + private void scanDataset(String taskId, String srcDatasetId) { + int pageNumber = 0; + int pageSize = 500; + PageRequest pageRequest = PageRequest.of(pageNumber, pageSize); + PagedDatasetFileResponse datasetFile; + do { + datasetFile = DatasetClient.getDatasetFile(srcDatasetId, pageRequest); + if (datasetFile.getContent() != null && datasetFile.getContent().isEmpty()) { + break; + } + List> files = datasetFile.getContent().stream() + .map(content -> Map.of("fileName", (Object) content.getFileName(), + "fileSize", content.getFileSize(), + "filePath", content.getFilePath(), + "fileType", content.getFileType(), + "fileId", content.getId())) + .toList(); + writeListMapToJsonlFile(files, FLOW_PATH + "/" + taskId + "/dataset.jsonl"); + pageNumber += 1; + } while (pageNumber < datasetFile.getTotalPages()); + } + + private void writeListMapToJsonlFile(List> mapList, String fileName) { + ObjectMapper objectMapper = new ObjectMapper(); + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) { + if (!mapList.isEmpty()) { // 检查列表是否为空,避免异常 + String jsonString = objectMapper.writeValueAsString(mapList.get(0)); + writer.write(jsonString); + + for (int i = 1; i < mapList.size(); i++) { + writer.newLine(); + jsonString = objectMapper.writeValueAsString(mapList.get(i)); + writer.write(jsonString); + } + } + } catch (IOException e) { + log.error("Failed to prepare dataset.jsonl.", e); + throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR); + } + } + + public void stopTask(String taskId) { + taskScheduler.stopTask(taskId); + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTemplateService.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTemplateService.java new file mode 100644 index 0000000..a5faaf4 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/application/service/CleaningTemplateService.java @@ -0,0 +1,95 @@ +package com.datamate.cleaning.application.service; + + +import com.datamate.cleaning.domain.converter.OperatorInstanceConverter; +import com.datamate.cleaning.domain.model.OperatorInstancePo; +import com.datamate.cleaning.domain.model.TemplateWithInstance; +import com.datamate.cleaning.infrastructure.persistence.mapper.CleaningTemplateMapper; +import com.datamate.cleaning.infrastructure.persistence.mapper.OperatorInstanceMapper; +import com.datamate.cleaning.interfaces.dto.CleaningTemplate; +import com.datamate.cleaning.interfaces.dto.CreateCleaningTemplateRequest; +import com.datamate.cleaning.interfaces.dto.OperatorResponse; +import com.datamate.cleaning.interfaces.dto.UpdateCleaningTemplateRequest; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CleaningTemplateService { + private final CleaningTemplateMapper cleaningTemplateMapper; + + private final OperatorInstanceMapper operatorInstanceMapper; + + public List getTemplates(String keywords) { + List allOperators = cleaningTemplateMapper.findAllOperators(); + Map operatorsMap = allOperators.stream() + .collect(Collectors.toMap(OperatorResponse::getId, Function.identity())); + List allTemplates = cleaningTemplateMapper.findAllTemplates(keywords); + Map> templatesMap = allTemplates.stream() + .collect(Collectors.groupingBy(TemplateWithInstance::getId)); + return templatesMap.entrySet().stream().map(twi -> { + List value = twi.getValue(); + CleaningTemplate template = new CleaningTemplate(); + template.setId(twi.getKey()); + template.setName(value.get(0).getName()); + template.setDescription(value.get(0).getDescription()); + template.setInstance(value.stream().filter(v -> StringUtils.isNotBlank(v.getOperatorId())) + .sorted(Comparator.comparingInt(TemplateWithInstance::getOpIndex)) + .map(v -> { + OperatorResponse operator = operatorsMap.get(v.getOperatorId()); + if (StringUtils.isNotBlank(v.getSettingsOverride())) { + operator.setSettings(v.getSettingsOverride()); + } + return operator; + }).toList()); + template.setCreatedAt(value.get(0).getCreatedAt()); + template.setUpdatedAt(value.get(0).getUpdatedAt()); + return template; + }).toList(); + } + + @Transactional + public CleaningTemplate createTemplate(CreateCleaningTemplateRequest request) { + CleaningTemplate template = new CleaningTemplate(); + String templateId = UUID.randomUUID().toString(); + template.setId(templateId); + template.setName(request.getName()); + template.setDescription(request.getDescription()); + cleaningTemplateMapper.insertTemplate(template); + + List instancePos = request.getInstance().stream() + .map(OperatorInstanceConverter.INSTANCE::operatorToDo).toList(); + operatorInstanceMapper.insertInstance(templateId, instancePos); + return template; + } + + public CleaningTemplate getTemplate(String templateId) { + return cleaningTemplateMapper.findTemplateById(templateId); + } + + @Transactional + public CleaningTemplate updateTemplate(String templateId, UpdateCleaningTemplateRequest request) { + CleaningTemplate template = cleaningTemplateMapper.findTemplateById(templateId); + if (template != null) { + template.setName(request.getName()); + template.setDescription(request.getDescription()); + cleaningTemplateMapper.updateTemplate(template); + } + return template; + } + + @Transactional + public void deleteTemplate(String templateId) { + cleaningTemplateMapper.deleteTemplate(templateId); + operatorInstanceMapper.deleteByInstanceId(templateId); + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/converter/OperatorInstanceConverter.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/converter/OperatorInstanceConverter.java new file mode 100644 index 0000000..d70181d --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/converter/OperatorInstanceConverter.java @@ -0,0 +1,33 @@ +package com.datamate.cleaning.domain.converter; + + +import com.datamate.cleaning.domain.model.OperatorInstancePo; +import com.datamate.cleaning.interfaces.dto.OperatorInstance; +import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.Map; + +@Mapper +public interface OperatorInstanceConverter { + OperatorInstanceConverter INSTANCE = Mappers.getMapper(OperatorInstanceConverter.class); + + @Mapping(target = "overrides", source = "overrides", qualifiedByName = "mapToJson") + OperatorInstancePo operatorToDo(OperatorInstance instance); + + @Named("mapToJson") + static String mapToJson(Map objects) { + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.writeValueAsString(objects); + } catch (JsonProcessingException e) { + throw BusinessException.of(SystemErrorCode.UNKNOWN_ERROR); + } + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/CreateDatasetRequest.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/CreateDatasetRequest.java new file mode 100644 index 0000000..5c19006 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/CreateDatasetRequest.java @@ -0,0 +1,26 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + + +@Getter +@Setter +@NoArgsConstructor +public class CreateDatasetRequest { + /** 数据集名称 */ + private String name; + /** 数据集描述 */ + private String description; + /** 数据集类型 */ + private String datasetType; + /** 标签列表 */ + private List tags; + /** 数据源 */ + private String dataSource; + /** 目标位置 */ + private String targetLocation; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetFileResponse.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetFileResponse.java new file mode 100644 index 0000000..e313373 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetFileResponse.java @@ -0,0 +1,36 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + + +@Getter +@Setter +@NoArgsConstructor +public class DatasetFileResponse { + /** 文件ID */ + private String id; + /** 文件名 */ + private String fileName; + /** 原始文件名 */ + private String originalName; + /** 文件类型 */ + private String fileType; + /** 文件大小(字节) */ + private Long fileSize; + /** 文件状态 */ + private String status; + /** 文件描述 */ + private String description; + /** 文件路径 */ + private String filePath; + /** 上传时间 */ + private LocalDateTime uploadTime; + /** 最后更新时间 */ + private LocalDateTime lastAccessTime; + /** 上传者 */ + private String uploadedBy; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetResponse.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetResponse.java new file mode 100644 index 0000000..2c63a0e --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetResponse.java @@ -0,0 +1,44 @@ +package com.datamate.cleaning.domain.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +/** + * 数据集实体(与数据库表 t_dm_datasets 对齐) + */ +@Getter +@Setter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class DatasetResponse { + /** 数据集ID */ + private String id; + /** 数据集名称 */ + private String name; + /** 数据集描述 */ + private String description; + /** 数据集类型 */ + private String datasetType; + /** 数据集状态 */ + private String status; + /** 数据源 */ + private String dataSource; + /** 目标位置 */ + private String targetLocation; + /** 文件数量 */ + private Integer fileCount; + /** 总大小(字节) */ + private Long totalSize; + /** 完成率(0-100) */ + private Float completionRate; + /** 创建时间 */ + private LocalDateTime createdAt; + /** 更新时间 */ + private LocalDateTime updatedAt; + /** 创建者 */ + private String createdBy; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetTypeResponse.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetTypeResponse.java new file mode 100644 index 0000000..b085583 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/DatasetTypeResponse.java @@ -0,0 +1,23 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +/** + * 数据集类型响应DTO + */ +@Getter +@Setter +public class DatasetTypeResponse { + /** 类型编码 */ + private String code; + /** 类型名称 */ + private String name; + /** 类型描述 */ + private String description; + /** 支持的文件格式 */ + private List supportedFormats; + /** 图标 */ + private String icon; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/ExecutorType.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/ExecutorType.java new file mode 100644 index 0000000..8ec2043 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/ExecutorType.java @@ -0,0 +1,25 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; + +@Getter +public enum ExecutorType { + DATA_PLATFORM("data_platform"), + DATA_JUICER_RAY("ray"), + DATA_JUICER_DEFAULT("default"); + + private final String value; + + ExecutorType(String value) { + this.value = value; + } + + public static ExecutorType fromValue(String value) { + for (ExecutorType type : ExecutorType.values()) { + if (type.value.equals(value)) { + return type; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/OperatorInstancePo.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/OperatorInstancePo.java new file mode 100644 index 0000000..2565a48 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/OperatorInstancePo.java @@ -0,0 +1,13 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; +import lombok.Setter; + + +@Getter +@Setter +public class OperatorInstancePo { + private String id; + + private String overrides; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/PagedDatasetFileResponse.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/PagedDatasetFileResponse.java new file mode 100644 index 0000000..da33f17 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/PagedDatasetFileResponse.java @@ -0,0 +1,28 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + + +@Getter +@Setter +@NoArgsConstructor +public class PagedDatasetFileResponse { + /** 文件内容列表 */ + private List content; + /** 当前页码 */ + private Integer page; + /** 每页大小 */ + private Integer size; + /** 总元素数 */ + private Integer totalElements; + /** 总页数 */ + private Integer totalPages; + /** 是否为第一页 */ + private Boolean first; + /** 是否为最后一页 */ + private Boolean last; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TaskProcess.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TaskProcess.java new file mode 100644 index 0000000..4cd61a2 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TaskProcess.java @@ -0,0 +1,24 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + + +@Getter +@Setter +public class TaskProcess { + private String instanceId; + + private String datasetId; + + private String datasetPath; + + private String exportPath; + + private String executorType; + + private List>> process; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TemplateWithInstance.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TemplateWithInstance.java new file mode 100644 index 0000000..0c12f3f --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/domain/model/TemplateWithInstance.java @@ -0,0 +1,30 @@ +package com.datamate.cleaning.domain.model; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + + +@Getter +@Setter +public class TemplateWithInstance { + private String id; + + private String name; + + private String description; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime createdAt; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime updatedAt; + + private String operatorId; + + private Integer opIndex; + + private String settingsOverride; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/exception/CleanErrorCode.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/exception/CleanErrorCode.java new file mode 100644 index 0000000..a00b49b --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/exception/CleanErrorCode.java @@ -0,0 +1,19 @@ +package com.datamate.cleaning.infrastructure.exception; + +import com.datamate.common.infrastructure.exception.ErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum CleanErrorCode implements ErrorCode { + /** + * 清洗任务名称重复 + */ + DUPLICATE_TASK_NAME("clean.0001", "清洗任务名称重复"), + + CREATE_DATASET_FAILED("clean.0002", "创建数据集失败"); + + private final String code; + private final String message; +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningResultMapper.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningResultMapper.java new file mode 100644 index 0000000..24548d8 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningResultMapper.java @@ -0,0 +1,9 @@ +package com.datamate.cleaning.infrastructure.persistence.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface CleaningResultMapper { + void deleteByInstanceId(@Param("instanceId") String instanceId); +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTaskMapper.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTaskMapper.java new file mode 100644 index 0000000..9650d30 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTaskMapper.java @@ -0,0 +1,21 @@ +package com.datamate.cleaning.infrastructure.persistence.mapper; + +import com.datamate.cleaning.interfaces.dto.CleaningTask; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface CleaningTaskMapper { + List findTasks(@Param("status") String status, @Param("keywords") String keywords, + @Param("size") Integer size, @Param("offset") Integer offset); + + CleaningTask findTaskById(@Param("taskId") String taskId); + + void insertTask(CleaningTask task); + + void updateTask(CleaningTask task); + + void deleteTask(@Param("taskId") String taskId); +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTemplateMapper.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTemplateMapper.java new file mode 100644 index 0000000..d4fefa6 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/CleaningTemplateMapper.java @@ -0,0 +1,25 @@ +package com.datamate.cleaning.infrastructure.persistence.mapper; + +import com.datamate.cleaning.domain.model.TemplateWithInstance; +import com.datamate.cleaning.interfaces.dto.CleaningTemplate; +import com.datamate.cleaning.interfaces.dto.OperatorResponse; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface CleaningTemplateMapper { + + List findAllTemplates(@Param("keywords") String keywords); + + List findAllOperators(); + + CleaningTemplate findTemplateById(@Param("templateId") String templateId); + + void insertTemplate(CleaningTemplate template); + + void updateTemplate(CleaningTemplate template); + + void deleteTemplate(@Param("templateId") String templateId); +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/OperatorInstanceMapper.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/OperatorInstanceMapper.java new file mode 100644 index 0000000..c890435 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/infrastructure/persistence/mapper/OperatorInstanceMapper.java @@ -0,0 +1,17 @@ +package com.datamate.cleaning.infrastructure.persistence.mapper; + +import com.datamate.cleaning.domain.model.OperatorInstancePo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + + +@Mapper +public interface OperatorInstanceMapper { + + void insertInstance(@Param("instanceId") String instanceId, + @Param("instances") List instances); + + void deleteByInstanceId(@Param("instanceId") String instanceId); +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTaskController.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTaskController.java new file mode 100644 index 0000000..641f549 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTaskController.java @@ -0,0 +1,59 @@ +package com.datamate.cleaning.interfaces.api; + +import com.datamate.cleaning.application.service.CleaningTaskService; +import com.datamate.cleaning.interfaces.dto.CleaningTask; +import com.datamate.cleaning.interfaces.dto.CreateCleaningTaskRequest; +import com.datamate.common.infrastructure.common.Response; +import com.datamate.common.interfaces.PagedResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +@RestController +@RequestMapping("/cleaning/tasks") +@RequiredArgsConstructor +public class CleaningTaskController { + private final CleaningTaskService cleaningTaskService; + + @GetMapping + public ResponseEntity>> cleaningTasksGet( + @RequestParam("page") Integer page, + @RequestParam("size") Integer size, @RequestParam(value = "status", required = false) String status, + @RequestParam(value = "keywords", required = false) String keywords) { + List tasks = cleaningTaskService.getTasks(status, keywords, page, size); + int count = cleaningTaskService.countTasks(status, keywords); + int totalPages = (count + size + 1) / size; + return ResponseEntity.ok(Response.ok(PagedResponse.of(tasks, page, count, totalPages))); + } + + @PostMapping + public ResponseEntity> cleaningTasksPost(@RequestBody CreateCleaningTaskRequest request) { + return ResponseEntity.ok(Response.ok(cleaningTaskService.createTask(request))); + } + + @PostMapping("/{taskId}/stop") + public ResponseEntity> cleaningTasksStop(@PathVariable("taskId") String taskId) { + cleaningTaskService.stopTask(taskId); + return ResponseEntity.ok(Response.ok(null)); + } + + @PostMapping("/{taskId}/execute") + public ResponseEntity> cleaningTasksStart(@PathVariable("taskId") String taskId) { + cleaningTaskService.executeTask(taskId); + return ResponseEntity.ok(Response.ok(null)); + } + + @GetMapping("/{taskId}") + public ResponseEntity> cleaningTasksTaskIdGet(@PathVariable("taskId") String taskId) { + return ResponseEntity.ok(Response.ok(cleaningTaskService.getTask(taskId))); + } + + @DeleteMapping("/{taskId}") + public ResponseEntity> cleaningTasksTaskIdDelete(@PathVariable("taskId") String taskId) { + cleaningTaskService.deleteTask(taskId); + return ResponseEntity.ok(Response.ok(null)); + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTemplateController.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTemplateController.java new file mode 100644 index 0000000..d73efe3 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/api/CleaningTemplateController.java @@ -0,0 +1,74 @@ +package com.datamate.cleaning.interfaces.api; + +import com.datamate.cleaning.application.service.CleaningTemplateService; +import com.datamate.cleaning.interfaces.dto.CleaningTemplate; +import com.datamate.cleaning.interfaces.dto.CreateCleaningTemplateRequest; +import com.datamate.cleaning.interfaces.dto.UpdateCleaningTemplateRequest; +import com.datamate.common.infrastructure.common.Response; +import com.datamate.common.interfaces.PagedResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Comparator; +import java.util.List; + + +@RestController +@RequestMapping("/cleaning/templates") +@RequiredArgsConstructor +public class CleaningTemplateController { + private final CleaningTemplateService cleaningTemplateService; + + @GetMapping + public ResponseEntity>> cleaningTemplatesGet( + @RequestParam(value = "page", required = false) Integer page, + @RequestParam(value = "size", required = false) Integer size, + @RequestParam(value = "keywords", required = false) String keyword) { + List templates = cleaningTemplateService.getTemplates(keyword); + if (page == null || size == null) { + return ResponseEntity.ok(Response.ok(PagedResponse.of(templates.stream() + .sorted(Comparator.comparing(CleaningTemplate::getCreatedAt).reversed()).toList()))); + } + int count = templates.size(); + int totalPages = (count + size + 1) / size; + List limitTemplates = templates.stream() + .sorted(Comparator.comparing(CleaningTemplate::getCreatedAt).reversed()) + .skip((long) page * size) + .limit(size).toList(); + return ResponseEntity.ok(Response.ok(PagedResponse.of(limitTemplates, page, count, totalPages))); + } + + @PostMapping + public ResponseEntity> cleaningTemplatesPost( + @RequestBody CreateCleaningTemplateRequest request) { + return ResponseEntity.ok(Response.ok(cleaningTemplateService.createTemplate(request))); + } + + @GetMapping("/{templateId}") + public ResponseEntity> cleaningTemplatesTemplateIdGet( + @PathVariable("templateId") String templateId) { + return ResponseEntity.ok(Response.ok(cleaningTemplateService.getTemplate(templateId))); + } + + @PutMapping("/{templateId}") + public ResponseEntity> cleaningTemplatesTemplateIdPut( + @PathVariable("templateId") String templateId, @RequestBody UpdateCleaningTemplateRequest request) { + return ResponseEntity.ok(Response.ok(cleaningTemplateService.updateTemplate(templateId, request))); + } + + @DeleteMapping("/{templateId}") + public ResponseEntity> cleaningTemplatesTemplateIdDelete( + @PathVariable("templateId") String templateId) { + cleaningTemplateService.deleteTemplate(templateId); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningProcess.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningProcess.java new file mode 100644 index 0000000..760c2bf --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningProcess.java @@ -0,0 +1,20 @@ +package com.datamate.cleaning.interfaces.dto; + + +import lombok.Getter; +import lombok.Setter; + +/** + * CleaningProcess + */ + +@Getter +@Setter +public class CleaningProcess { + private Float process; + + private Integer totalFileNum; + + private Integer finishedFileNum; +} + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTask.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTask.java new file mode 100644 index 0000000..2ee603c --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTask.java @@ -0,0 +1,92 @@ +package com.datamate.cleaning.interfaces.dto; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.time.LocalDateTime; +import java.util.List; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * CleaningTask + */ + +@Getter +@Setter +public class CleaningTask { + + private String id; + + private String name; + + private String description; + + private String srcDatasetId; + + private String srcDatasetName; + + private String destDatasetId; + + private String destDatasetName; + + private long beforeSize; + + private long afterSize; + + /** + * 任务当前状态 + */ + public enum StatusEnum { + PENDING("PENDING"), + + RUNNING("RUNNING"), + + COMPLETED("COMPLETED"), + + STOPPED("STOPPED"), + + FAILED("FAILED"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private StatusEnum status; + + private String templateId; + + private List instance; + + private CleaningProcess progress; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime createdAt; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime startedAt; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime finishedAt; +} + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTemplate.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTemplate.java new file mode 100644 index 0000000..e95a813 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CleaningTemplate.java @@ -0,0 +1,33 @@ +package com.datamate.cleaning.interfaces.dto; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * CleaningTemplate + */ + +@Getter +@Setter +public class CleaningTemplate { + + private String id; + + private String name; + + private String description; + + private List instance = new ArrayList<>(); + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime createdAt; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime updatedAt; +} + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTaskRequest.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTaskRequest.java new file mode 100644 index 0000000..8ce1be4 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTaskRequest.java @@ -0,0 +1,32 @@ +package com.datamate.cleaning.interfaces.dto; + +import java.util.ArrayList; +import java.util.List; + + +import lombok.Getter; +import lombok.Setter; + +/** + * CreateCleaningTaskRequest + */ + +@Getter +@Setter +public class CreateCleaningTaskRequest { + + private String name; + + private String description; + + private String srcDatasetId; + + private String srcDatasetName; + + private String destDatasetName; + + private String destDatasetType; + + private List instance = new ArrayList<>(); +} + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTemplateRequest.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTemplateRequest.java new file mode 100644 index 0000000..16081fd --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/CreateCleaningTemplateRequest.java @@ -0,0 +1,23 @@ +package com.datamate.cleaning.interfaces.dto; + +import java.util.ArrayList; +import java.util.List; + +import lombok.Getter; +import lombok.Setter; + +/** + * CreateCleaningTemplateRequest + */ + +@Getter +@Setter +public class CreateCleaningTemplateRequest { + + private String name; + + private String description; + + private List instance = new ArrayList<>(); +} + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorInstance.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorInstance.java new file mode 100644 index 0000000..f65c2e6 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorInstance.java @@ -0,0 +1,22 @@ +package com.datamate.cleaning.interfaces.dto; + +import java.util.HashMap; +import java.util.Map; + + +import lombok.Getter; +import lombok.Setter; + +/** + * OperatorInstance + */ + +@Getter +@Setter +public class OperatorInstance { + + private String id; + + private Map overrides = new HashMap<>(); +} + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorResponse.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorResponse.java new file mode 100644 index 0000000..667bd42 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/OperatorResponse.java @@ -0,0 +1,41 @@ +package com.datamate.cleaning.interfaces.dto; + +import java.time.LocalDateTime; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * OperatorResponse + */ + +@Getter +@Setter +public class OperatorResponse { + + private String id; + + private String name; + + private String description; + + private String version; + + private String inputs; + + private String outputs; + + private String runtime; + + private String settings; + + private Boolean isStar; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime createdAt; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime updatedAt; +} + diff --git a/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/UpdateCleaningTemplateRequest.java b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/UpdateCleaningTemplateRequest.java new file mode 100644 index 0000000..f1037e1 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/java/com/datamate/cleaning/interfaces/dto/UpdateCleaningTemplateRequest.java @@ -0,0 +1,26 @@ +package com.datamate.cleaning.interfaces.dto; + +import java.util.ArrayList; +import java.util.List; + + +import lombok.Getter; +import lombok.Setter; + +/** + * UpdateCleaningTemplateRequest + */ + +@Getter +@Setter +public class UpdateCleaningTemplateRequest { + + private String id; + + private String name; + + private String description; + + private List instance = new ArrayList<>(); +} + diff --git a/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningResultMapper.xml b/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningResultMapper.xml new file mode 100644 index 0000000..b3d6446 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningResultMapper.xml @@ -0,0 +1,8 @@ + + + + + DELETE FROM t_clean_result WHERE instance_id = #{instanceId} + + + diff --git a/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTaskMapper.xml b/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTaskMapper.xml new file mode 100644 index 0000000..7a16f73 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTaskMapper.xml @@ -0,0 +1,56 @@ + + + + + id, name, description, src_dataset_id, src_dataset_name, dest_dataset_id, dest_dataset_name, before_size, + after_size, status, created_at, started_at, finished_at + + + + + + + + INSERT INTO t_clean_task (id, name, description, status, src_dataset_id, src_dataset_name, dest_dataset_id, + dest_dataset_name, before_size, after_size, created_at) + VALUES (#{id}, #{name}, #{description}, #{status}, #{srcDatasetId}, #{srcDatasetName}, #{destDatasetId}, + #{destDatasetName}, ${beforeSize}, ${afterSize}, NOW()) + + + + UPDATE t_clean_task + + + status = #{status.value}, + + + started_at = #{startedAt}, + + + finished_at = #{finishedAt}, + + + WHERE id = #{id} + + + + DELETE FROM t_clean_task WHERE id = #{taskId} + + + diff --git a/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTemplateMapper.xml b/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTemplateMapper.xml new file mode 100644 index 0000000..6c01b7b --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/resources/mappers/CleaningTemplateMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + INSERT INTO t_clean_template (id, name, description, created_at) + VALUES (#{id}, #{name}, #{description}, NOW()) + + + + UPDATE t_clean_template SET name = #{name}, description = #{description}, updated_at = NOW() WHERE id = #{id} + + + + DELETE FROM t_clean_template WHERE id = #{templateId} + + + diff --git a/backend/services/data-cleaning-service/src/main/resources/mappers/OperatorInstanceMapper.xml b/backend/services/data-cleaning-service/src/main/resources/mappers/OperatorInstanceMapper.xml new file mode 100644 index 0000000..e56b0b3 --- /dev/null +++ b/backend/services/data-cleaning-service/src/main/resources/mappers/OperatorInstanceMapper.xml @@ -0,0 +1,16 @@ + + + + + INSERT INTO t_operator_instance(instance_id, operator_id, op_index, settings_override) + VALUES + + (#{instanceId}, #{operator.id}, #{index} + 1, #{operator.overrides}) + + + + + DELETE FROM t_operator_instance + WHERE instance_id = #{instanceId}; + + diff --git a/backend/services/data-collection-service/README.md b/backend/services/data-collection-service/README.md new file mode 100644 index 0000000..773b570 --- /dev/null +++ b/backend/services/data-collection-service/README.md @@ -0,0 +1,229 @@ +# 数据归集服务 (Data Collection Service) + +基于DataX的数据归集和同步服务,提供多数据源之间的数据同步功能。 + +## 功能特性 + +- 🔗 **多数据源支持**: 支持MySQL、PostgreSQL、Oracle、SQL Server等主流数据库 +- 📊 **任务管理**: 创建、配置、执行和监控数据同步任务 +- ⏰ **定时调度**: 支持Cron表达式的定时任务 +- 📈 **实时监控**: 任务执行进度、状态和性能指标监控 +- 📝 **执行日志**: 详细的任务执行日志记录 +- 🔌 **插件化**: DataX Reader/Writer插件化集成 + +## 技术架构 + +- **框架**: Spring Boot 3.x +- **数据库**: MySQL + MyBatis +- **同步引擎**: DataX +- **API**: OpenAPI 3.0 自动生成 +- **架构模式**: DDD (领域驱动设计) + +## 项目结构 + +``` +src/main/java/com/datamate/collection/ +├── DataCollectionApplication.java # 应用启动类 +├── domain/ # 领域层 +│ ├── model/ # 领域模型 +│ │ ├── DataSource.java # 数据源实体 +│ │ ├── CollectionTask.java # 归集任务实体 +│ │ ├── TaskExecution.java # 任务执行记录 +│ │ └── ExecutionLog.java # 执行日志 +│ └── service/ # 领域服务 +│ ├── DataSourceService.java +│ ├── CollectionTaskService.java +│ ├── TaskExecutionService.java +│ └── impl/ # 服务实现 +├── infrastructure/ # 基础设施层 +│ ├── config/ # 配置类 +│ ├── datax/ # DataX执行引擎 +│ │ └── DataXExecutionEngine.java +│ └── persistence/ # 持久化 +│ ├── mapper/ # MyBatis Mapper +│ └── typehandler/ # 类型处理器 +└── interfaces/ # 接口层 + ├── api/ # OpenAPI生成的接口 + ├── dto/ # OpenAPI生成的DTO + └── rest/ # REST控制器 + ├── DataSourceController.java + ├── CollectionTaskController.java + ├── TaskExecutionController.java + └── exception/ # 异常处理 + +src/main/resources/ +├── mappers/ # MyBatis XML映射文件 +├── application.properties # 应用配置 +└── ... +``` + +## 环境要求 + +- Java 17+ +- Maven 3.6+ +- MySQL 8.0+ +- DataX 3.0+ +- Redis (可选,用于缓存) + +## 配置说明 + +### 应用配置 (application.properties) + +```properties +# 服务端口 +server.port=8090 + +# 数据库配置 +spring.datasource.url=jdbc:mysql://localhost:3306/knowledge_base +spring.datasource.username=root +spring.datasource.password=123456 + +# DataX配置 +datax.home=/runtime/datax +datax.python.path=/runtime/datax/bin/datax.py +datax.job.timeout=7200 +datax.job.memory=2g +``` + +### DataX配置 + +确保DataX已正确安装并配置: + +1. 下载DataX到 `/runtime/datax` 目录 +2. 配置相关Reader/Writer插件 +3. 确保Python环境可用 + +## 数据库初始化 + +执行数据库初始化脚本: + +```bash +mysql -u root -p knowledge_base < scripts/db/data-collection-init.sql +``` + +## 构建和运行 + +### 1. 编译项目 + +```bash +cd backend/services/data-collection-service +mvn clean compile +``` + +这将触发OpenAPI代码生成。 + +### 2. 打包 + +```bash +mvn clean package -DskipTests +``` + +### 3. 运行 + +作为独立服务运行: +```bash +java -jar target/data-collection-service-1.0.0-SNAPSHOT.jar +``` + +或通过main-application统一启动: +```bash +cd backend/services/main-application +mvn spring-boot:run +``` + +## API文档 + +服务启动后,可通过以下地址访问API文档: + +- Swagger UI: http://localhost:8090/swagger-ui.html +- OpenAPI JSON: http://localhost:8090/v3/api-docs + +## 主要API端点 + +### 数据源管理 + +- `GET /api/v1/collection/datasources` - 获取数据源列表 +- `POST /api/v1/collection/datasources` - 创建数据源 +- `GET /api/v1/collection/datasources/{id}` - 获取数据源详情 +- `PUT /api/v1/collection/datasources/{id}` - 更新数据源 +- `DELETE /api/v1/collection/datasources/{id}` - 删除数据源 +- `POST /api/v1/collection/datasources/{id}/test` - 测试连接 + +### 归集任务管理 + +- `GET /api/v1/collection/tasks` - 获取任务列表 +- `POST /api/v1/collection/tasks` - 创建任务 +- `GET /api/v1/collection/tasks/{id}` - 获取任务详情 +- `PUT /api/v1/collection/tasks/{id}` - 更新任务 +- `DELETE /api/v1/collection/tasks/{id}` - 删除任务 + +### 任务执行管理 + +- `POST /api/v1/collection/tasks/{id}/execute` - 执行任务 +- `POST /api/v1/collection/tasks/{id}/stop` - 停止任务 +- `GET /api/v1/collection/executions` - 获取执行历史 +- `GET /api/v1/collection/executions/{executionId}` - 获取执行详情 +- `GET /api/v1/collection/executions/{executionId}/logs` - 获取执行日志 + +### 监控统计 + +- `GET /api/v1/collection/monitor/statistics` - 获取统计信息 + +## 开发指南 + +### 添加新的数据源类型 + +1. 在 `DataSource.DataSourceType` 枚举中添加新类型 +2. 在 `DataXExecutionEngine` 中添加对应的Reader/Writer映射 +3. 更新数据库表结构和初始化数据 + +### 自定义DataX插件 + +1. 将插件放置在 `/runtime/datax/plugin` 目录下 +2. 在 `DataXExecutionEngine` 中配置插件映射关系 +3. 根据插件要求调整配置模板 + +### 扩展监控指标 + +1. 在 `StatisticsService` 中添加新的统计逻辑 +2. 更新 `CollectionStatistics` DTO +3. 在数据库中添加相应的统计表或字段 + +## 故障排查 + +### 常见问题 + +1. **DataX执行失败** + - 检查DataX安装路径和Python环境 + - 确认数据源连接配置正确 + - 查看执行日志获取详细错误信息 + +2. **数据库连接失败** + - 检查数据库配置和网络连通性 + - 确认数据库用户权限 + +3. **API调用失败** + - 检查请求参数格式 + - 查看应用日志获取详细错误信息 + +### 日志查看 + +```bash +# 应用日志 +tail -f logs/data-collection-service.log + +# 任务执行日志 +curl http://localhost:8090/api/v1/collection/executions/{executionId}/logs +``` + +## 贡献指南 + +1. Fork项目 +2. 创建特性分支: `git checkout -b feature/new-feature` +3. 提交更改: `git commit -am 'Add new feature'` +4. 推送分支: `git push origin feature/new-feature` +5. 提交Pull Request + +## 许可证 + +MIT License diff --git a/backend/services/data-collection-service/image.png b/backend/services/data-collection-service/image.png new file mode 100644 index 0000000000000000000000000000000000000000..4584a6fd434fdffa332652d6e4cfe2398e3cb96b GIT binary patch literal 80705 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

BX*qu+E(}C;#XqFmY0yaKmW^*44AWOuv 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 G<9`4|DN;-T literal 0 HcmV?d00001 diff --git a/backend/services/data-collection-service/image1.png b/backend/services/data-collection-service/image1.png new file mode 100644 index 0000000000000000000000000000000000000000..772c9d4fed2625bb5cface9693899937f3c2ddd9 GIT binary patch literal 53113 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≦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 R<^cF7dsFd7=5?b-{|7Ag1zi9D literal 0 HcmV?d00001 diff --git a/backend/services/data-collection-service/pom.xml b/backend/services/data-collection-service/pom.xml new file mode 100644 index 0000000..1ea9d3c --- /dev/null +++ b/backend/services/data-collection-service/pom.xml @@ -0,0 +1,200 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + data-collection-service + jar + Data Collection Service + DataX-based data collection and aggregation service + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + + mysql + mysql-connector-java + 8.0.33 + runtime + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-exec + 1.3 + + + + + com.zaxxer + HikariCP + + + + + com.oracle.database.jdbc + ojdbc8 + 21.5.0.0 + + + + + org.postgresql + postgresql + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.datamate + domain-common + 1.0.0-SNAPSHOT + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.openapitools + jackson-databind-nullable + + + jakarta.validation + jakarta.validation-api + + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + commons-io + commons-io + 2.16.1 + compile + + + + + + + + org.openapitools + openapi-generator-maven-plugin + 6.6.0 + + + + generate + + + ${project.basedir}/../../openapi/specs/data-collection.yaml + spring + ${project.build.directory}/generated-sources/openapi + com.datamate.collection.interfaces.api + com.datamate.collection.interfaces.dto + + true + true + true + springdoc + java8-localdatetime + true + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + exec + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + ${lombok-mapstruct-binding.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + -parameters + -Amapstruct.defaultComponentModel=spring + + + + + + diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/DataCollectionServiceConfiguration.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/DataCollectionServiceConfiguration.java new file mode 100644 index 0000000..f22c5b9 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/DataCollectionServiceConfiguration.java @@ -0,0 +1,24 @@ +package com.datamate.collection; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * 数据归集服务配置类 + * + * 基于DataX的数据归集和同步服务,支持多种数据源的数据采集和归集 + */ +@SpringBootApplication +@EnableAsync +@EnableScheduling +@EnableTransactionManagement +@ComponentScan(basePackages = { + "com.datamate.collection", + "com.datamate.shared" +}) +public class DataCollectionServiceConfiguration { + // Configuration class for JAR packaging - no main method needed +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/scheduler/TaskSchedulerInitializer.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/scheduler/TaskSchedulerInitializer.java new file mode 100644 index 0000000..0203cab --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/scheduler/TaskSchedulerInitializer.java @@ -0,0 +1,66 @@ +package com.datamate.collection.application.scheduler; + +import com.datamate.collection.application.service.DataxExecutionService; +import com.datamate.collection.domain.model.CollectionTask; +import com.datamate.collection.domain.model.TaskStatus; +import com.datamate.collection.domain.model.TaskExecution; +import com.datamate.collection.infrastructure.persistence.mapper.CollectionTaskMapper; +import com.datamate.collection.infrastructure.persistence.mapper.TaskExecutionMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.support.CronExpression; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class TaskSchedulerInitializer { + + private final CollectionTaskMapper taskMapper; + private final TaskExecutionMapper executionMapper; + private final DataxExecutionService dataxExecutionService; + + // 定期扫描激活的采集任务,根据 Cron 判断是否到期执行 + @Scheduled(fixedDelayString = "${datamate.data-collection.scheduler.scan-interval-ms:10000}") + public void scanAndTrigger() { + List tasks = taskMapper.selectActiveTasks(); + if (tasks == null || tasks.isEmpty()) { + return; + } + LocalDateTime now = LocalDateTime.now(); + for (CollectionTask task : tasks) { + String cronExpr = task.getScheduleExpression(); + if (!StringUtils.hasText(cronExpr)) { + continue; + } + try { + // 如果最近一次执行仍在运行,则跳过 + TaskExecution latest = executionMapper.selectLatestByTaskId(task.getId()); + if (latest != null && latest.getStatus() == TaskStatus.RUNNING) { + continue; + } + + CronExpression cron = CronExpression.parse(cronExpr); + LocalDateTime base = latest != null && latest.getStartedAt() != null + ? latest.getStartedAt() + : now.minusYears(1); // 没有历史记录时,拉长基准时间确保到期判定 + LocalDateTime nextTime = cron.next(base); + + if (nextTime != null && !nextTime.isAfter(now)) { + // 到期,触发一次执行 + TaskExecution exec = dataxExecutionService.createExecution(task); + int timeout = task.getTimeoutSeconds() == null ? 3600 : task.getTimeoutSeconds(); + dataxExecutionService.runAsync(task, exec.getId(), timeout); + log.info("Triggered DataX execution for task {} at {}, execId={}", task.getId(), now, exec.getId()); + } + } catch (Exception ex) { + log.warn("Skip task {} due to invalid cron or scheduling error: {}", task.getId(), ex.getMessage()); + } + } + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/CollectionTaskService.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/CollectionTaskService.java new file mode 100644 index 0000000..d2b6e21 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/CollectionTaskService.java @@ -0,0 +1,85 @@ +package com.datamate.collection.application.service; + +import com.datamate.collection.domain.model.CollectionTask; +import com.datamate.collection.domain.model.TaskExecution; +import com.datamate.collection.domain.model.TaskStatus; +import com.datamate.collection.domain.model.DataxTemplate; +import com.datamate.collection.infrastructure.persistence.mapper.CollectionTaskMapper; +import com.datamate.collection.infrastructure.persistence.mapper.TaskExecutionMapper; +import com.datamate.collection.interfaces.dto.SyncMode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CollectionTaskService { + private final CollectionTaskMapper taskMapper; + private final TaskExecutionMapper executionMapper; + private final DataxExecutionService dataxExecutionService; + + @Transactional + public CollectionTask create(CollectionTask task) { + task.setStatus(TaskStatus.READY); + task.setCreatedAt(LocalDateTime.now()); + task.setUpdatedAt(LocalDateTime.now()); + taskMapper.insert(task); + executeTaskNow(task); + return task; + } + + private void executeTaskNow(CollectionTask task) { + if (Objects.equals(task.getSyncMode(), SyncMode.ONCE.getValue())) { + TaskExecution exec = dataxExecutionService.createExecution(task); + int timeout = task.getTimeoutSeconds() == null ? 3600 : task.getTimeoutSeconds(); + dataxExecutionService.runAsync(task, exec.getId(), timeout); + log.info("Triggered DataX execution for task {} at {}, execId={}", task.getId(), LocalDateTime.now(), exec.getId()); + } + } + + @Transactional + public CollectionTask update(CollectionTask task) { + task.setUpdatedAt(LocalDateTime.now()); + taskMapper.update(task); + return task; + } + + @Transactional + public void delete(String id) { taskMapper.deleteById(id); } + + public CollectionTask get(String id) { return taskMapper.selectById(id); } + + public List list(Integer page, Integer size, String status, String name) { + Map p = new HashMap<>(); + p.put("status", status); + p.put("name", name); + if (page != null && size != null) { + p.put("offset", page * size); + p.put("limit", size); + } + return taskMapper.selectAll(p); + } + + @Transactional + public TaskExecution startExecution(CollectionTask task) { + return dataxExecutionService.createExecution(task); + } + + // ---- Template related merged methods ---- + public List listTemplates(String sourceType, String targetType, int page, int size) { + int offset = page * size; + return taskMapper.selectList(sourceType, targetType, offset, size); + } + + public int countTemplates(String sourceType, String targetType) { + return taskMapper.countTemplates(sourceType, targetType); + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/DataxExecutionService.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/DataxExecutionService.java new file mode 100644 index 0000000..fc2eb9e --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/DataxExecutionService.java @@ -0,0 +1,60 @@ +package com.datamate.collection.application.service; + +import com.datamate.collection.domain.model.CollectionTask; +import com.datamate.collection.domain.model.TaskExecution; +import com.datamate.collection.domain.model.TaskStatus; +import com.datamate.collection.infrastructure.persistence.mapper.CollectionTaskMapper; +import com.datamate.collection.infrastructure.persistence.mapper.TaskExecutionMapper; +import com.datamate.collection.infrastructure.runtime.datax.DataxJobBuilder; +import com.datamate.collection.infrastructure.runtime.datax.DataxProcessRunner; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.nio.file.Path; +import java.time.Duration; +import java.time.LocalDateTime; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DataxExecutionService { + + private final DataxJobBuilder jobBuilder; + private final DataxProcessRunner processRunner; + private final TaskExecutionMapper executionMapper; + private final CollectionTaskMapper taskMapper; + + + @Transactional + public TaskExecution createExecution(CollectionTask task) { + TaskExecution exec = TaskExecution.initTaskExecution(); + exec.setTaskId(task.getId()); + exec.setTaskName(task.getName()); + executionMapper.insert(exec); + taskMapper.updateLastExecution(task.getId(), exec.getId()); + taskMapper.updateStatus(task.getId(), TaskStatus.RUNNING.name()); + return exec; + } + + @Async + public void runAsync(CollectionTask task, String executionId, int timeoutSeconds) { + try { + Path job = jobBuilder.buildJobFile(task); + + int code = processRunner.runJob(job.toFile(), executionId, Duration.ofSeconds(timeoutSeconds)); + log.info("DataX finished with code {} for execution {}", code, executionId); + // 简化:成功即完成 + executionMapper.completeExecution(executionId, TaskStatus.SUCCESS.name(), LocalDateTime.now(), + 0, 0L, 0L, 0L, null, null); + taskMapper.updateStatus(task.getId(), TaskStatus.SUCCESS.name()); + } catch (Exception e) { + log.error("DataX execution failed", e); + executionMapper.completeExecution(executionId, TaskStatus.FAILED.name(), LocalDateTime.now(), + 0, 0L, 0L, 0L, e.getMessage(), null); + taskMapper.updateStatus(task.getId(), TaskStatus.FAILED.name()); + } + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/TaskExecutionService.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/TaskExecutionService.java new file mode 100644 index 0000000..68f9e3b --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/application/service/TaskExecutionService.java @@ -0,0 +1,83 @@ +package com.datamate.collection.application.service; + +import com.datamate.collection.domain.model.CollectionTask; +import com.datamate.collection.domain.model.TaskExecution; +import com.datamate.collection.domain.model.TaskStatus; +import com.datamate.collection.infrastructure.persistence.mapper.CollectionTaskMapper; +import com.datamate.collection.infrastructure.persistence.mapper.TaskExecutionMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class TaskExecutionService { + private final TaskExecutionMapper executionMapper; + private final CollectionTaskMapper taskMapper; + + public List list(String taskId, String status, LocalDateTime startDate, + LocalDateTime endDate, Integer page, Integer size) { + Map p = new HashMap<>(); + p.put("taskId", taskId); + p.put("status", status); + p.put("startDate", startDate); + p.put("endDate", endDate); + if (page != null && size != null) { + p.put("offset", page * size); + p.put("limit", size); + } + return executionMapper.selectAll(p); + } + + public long count(String taskId, String status, LocalDateTime startDate, LocalDateTime endDate) { + Map p = new HashMap<>(); + p.put("taskId", taskId); + p.put("status", status); + p.put("startDate", startDate); + p.put("endDate", endDate); + return executionMapper.count(p); + } + + // --- Added convenience methods --- + public TaskExecution get(String id) { return executionMapper.selectById(id); } + public TaskExecution getLatestByTaskId(String taskId) { return executionMapper.selectLatestByTaskId(taskId); } + + @Transactional + public void complete(String executionId, boolean success, long successCount, long failedCount, + long dataSizeBytes, String errorMessage, String resultJson) { + LocalDateTime now = LocalDateTime.now(); + TaskExecution exec = executionMapper.selectById(executionId); + if (exec == null) { return; } + int duration = (int) Duration.between(exec.getStartedAt(), now).getSeconds(); + executionMapper.completeExecution(executionId, success ? TaskStatus.SUCCESS.name() : TaskStatus.FAILED.name(), + now, duration, successCount, failedCount, dataSizeBytes, errorMessage, resultJson); + CollectionTask task = taskMapper.selectById(exec.getTaskId()); + if (task != null) { + taskMapper.updateStatus(task.getId(), success ? TaskStatus.SUCCESS.name() : TaskStatus.FAILED.name()); + } + } + + @Transactional + public void stop(String executionId) { + TaskExecution exec = executionMapper.selectById(executionId); + if (exec == null || exec.getStatus() != TaskStatus.RUNNING) { return; } + LocalDateTime now = LocalDateTime.now(); + int duration = (int) Duration.between(exec.getStartedAt(), now).getSeconds(); + // Reuse completeExecution to persist STOPPED status and timing info + executionMapper.completeExecution(exec.getId(), TaskStatus.STOPPED.name(), now, duration, + exec.getRecordsSuccess(), exec.getRecordsFailed(), exec.getDataSizeBytes(), null, exec.getResult()); + taskMapper.updateStatus(exec.getTaskId(), TaskStatus.STOPPED.name()); + } + + @Transactional + public void stopLatestByTaskId(String taskId) { + TaskExecution latest = executionMapper.selectLatestByTaskId(taskId); + if (latest != null) { stop(latest.getId()); } + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/CollectionTask.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/CollectionTask.java new file mode 100644 index 0000000..f40afa7 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/CollectionTask.java @@ -0,0 +1,45 @@ +package com.datamate.collection.domain.model; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Map; + +@Data +public class CollectionTask { + private String id; + private String name; + private String description; + private String config; // DataX JSON 配置,包含源端和目标端配置信息 + private TaskStatus status; + private String syncMode; // ONCE / SCHEDULED + private String scheduleExpression; + private Integer retryCount; + private Integer timeoutSeconds; + private Long maxRecords; + private String sortField; + private String lastExecutionId; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + private String createdBy; + private String updatedBy; + + public void addPath() { + try { + ObjectMapper objectMapper = new ObjectMapper(); + Map parameter = objectMapper.readValue( + config, + new TypeReference<>() {} + ); + parameter.put("destPath", "/dataset/local/" + id); + parameter.put("filePaths", Collections.singletonList(parameter.get("destPath"))); + config = objectMapper.writeValueAsString(parameter); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/DataxTemplate.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/DataxTemplate.java new file mode 100644 index 0000000..c537a67 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/DataxTemplate.java @@ -0,0 +1,71 @@ +package com.datamate.collection.domain.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +@Data +@EqualsAndHashCode(callSuper = false) +public class DataxTemplate { + + /** + * 模板ID(UUID) + */ + private String id; + + /** + * 模板名称 + */ + private String name; + + /** + * 源数据源类型 + */ + private String sourceType; + + /** + * 目标数据源类型 + */ + private String targetType; + + /** + * 模板内容(JSON格式) + */ + private String templateContent; + + /** + * 模板描述 + */ + private String description; + + /** + * 版本号 + */ + private String version; + + /** + * 是否为系统模板 + */ + private Boolean isSystem; + + /** + * 创建时间 + */ + private LocalDateTime createdAt; + + /** + * 更新时间 + */ + private LocalDateTime updatedAt; + + /** + * 创建者 + */ + private String createdBy; + + /** + * 更新者 + */ + private String updatedBy; +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskExecution.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskExecution.java new file mode 100644 index 0000000..05f1fe8 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskExecution.java @@ -0,0 +1,39 @@ +package com.datamate.collection.domain.model; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +public class TaskExecution { + private String id; + private String taskId; + private String taskName; + private TaskStatus status; + private Double progress; + private Long recordsTotal; + private Long recordsProcessed; + private Long recordsSuccess; + private Long recordsFailed; + private Double throughput; + private Long dataSizeBytes; + private LocalDateTime startedAt; + private LocalDateTime completedAt; + private Integer durationSeconds; + private String errorMessage; + private String dataxJobId; + private String config; + private String result; + private LocalDateTime createdAt; + + public static TaskExecution initTaskExecution() { + TaskExecution exec = new TaskExecution(); + exec.setId(UUID.randomUUID().toString()); + exec.setStatus(TaskStatus.RUNNING); + exec.setProgress(0.0); + exec.setStartedAt(LocalDateTime.now()); + exec.setCreatedAt(LocalDateTime.now()); + return exec; + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskStatus.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskStatus.java new file mode 100644 index 0000000..c5273d2 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/domain/model/TaskStatus.java @@ -0,0 +1,21 @@ +package com.datamate.collection.domain.model; + +/** + * 统一的任务和执行状态枚举 + * + * @author Data Mate Platform Team + */ +public enum TaskStatus { + /** 草稿状态 */ + DRAFT, + /** 就绪状态 */ + READY, + /** 运行中 */ + RUNNING, + /** 执行成功(对应原来的COMPLETED) */ + SUCCESS, + /** 执行失败 */ + FAILED, + /** 已停止 */ + STOPPED +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/CollectionTaskMapper.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/CollectionTaskMapper.java new file mode 100644 index 0000000..9359cc9 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/CollectionTaskMapper.java @@ -0,0 +1,47 @@ +package com.datamate.collection.infrastructure.persistence.mapper; + +import com.datamate.collection.domain.model.CollectionTask; +import com.datamate.collection.domain.model.DataxTemplate; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface CollectionTaskMapper { + int insert(CollectionTask entity); + int update(CollectionTask entity); + int deleteById(@Param("id") String id); + CollectionTask selectById(@Param("id") String id); + CollectionTask selectByName(@Param("name") String name); + List selectByStatus(@Param("status") String status); + List selectAll(Map params); + int updateStatus(@Param("id") String id, @Param("status") String status); + int updateLastExecution(@Param("id") String id, @Param("lastExecutionId") String lastExecutionId); + List selectActiveTasks(); + + /** + * 查询模板列表 + * + * @param sourceType 源数据源类型(可选) + * @param targetType 目标数据源类型(可选) + * @param offset 偏移量 + * @param limit 限制数量 + * @return 模板列表 + */ + List selectList(@Param("sourceType") String sourceType, + @Param("targetType") String targetType, + @Param("offset") int offset, + @Param("limit") int limit); + + /** + * 统计模板数量 + * + * @param sourceType 源数据源类型(可选) + * @param targetType 目标数据源类型(可选) + * @return 模板总数 + */ + int countTemplates(@Param("sourceType") String sourceType, + @Param("targetType") String targetType); +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/TaskExecutionMapper.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/TaskExecutionMapper.java new file mode 100644 index 0000000..645d885 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/mapper/TaskExecutionMapper.java @@ -0,0 +1,38 @@ +package com.datamate.collection.infrastructure.persistence.mapper; + +import com.datamate.collection.domain.model.TaskExecution; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Mapper +public interface TaskExecutionMapper { + int insert(TaskExecution entity); + int update(TaskExecution entity); + int deleteById(@Param("id") String id); + TaskExecution selectById(@Param("id") String id); + List selectByTaskId(@Param("taskId") String taskId, @Param("limit") Integer limit); + List selectByStatus(@Param("status") String status); + List selectAll(Map params); + long count(Map params); + int updateProgress(@Param("id") String id, + @Param("status") String status, + @Param("progress") Double progress, + @Param("recordsProcessed") Long recordsProcessed, + @Param("throughput") Double throughput); + int completeExecution(@Param("id") String id, + @Param("status") String status, + @Param("completedAt") LocalDateTime completedAt, + @Param("durationSeconds") Integer durationSeconds, + @Param("recordsSuccess") Long recordsSuccess, + @Param("recordsFailed") Long recordsFailed, + @Param("dataSizeBytes") Long dataSizeBytes, + @Param("errorMessage") String errorMessage, + @Param("result") String result); + List selectRunningExecutions(); + TaskExecution selectLatestByTaskId(@Param("taskId") String taskId); + int deleteOldExecutions(@Param("beforeDate") LocalDateTime beforeDate); +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/typehandler/TaskStatusTypeHandler.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/persistence/typehandler/TaskStatusTypeHandler.java new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxJobBuilder.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxJobBuilder.java new file mode 100644 index 0000000..db57d2a --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxJobBuilder.java @@ -0,0 +1,83 @@ +package com.datamate.collection.infrastructure.runtime.datax; + +import com.datamate.collection.domain.model.CollectionTask; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 根据任务配置拼装 DataX 作业 JSON 文件 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DataxJobBuilder { + + private final DataxProperties props; + + public Path buildJobFile(CollectionTask task) throws IOException { + Files.createDirectories(Paths.get(props.getJobConfigPath())); + String fileName = String.format("datax-job-%s.json", task.getId()); + Path path = Paths.get(props.getJobConfigPath(), fileName); + // 简化:直接将任务中的 config 字段作为 DataX 作业 JSON + try (FileWriter fw = new FileWriter(path.toFile())) { + String json = task.getConfig() == null || task.getConfig().isEmpty() ? + defaultJobJson() : task.getConfig(); + if (StringUtils.isNotBlank(task.getConfig())) { + json = getJobConfig(task); + } + log.info("Job config: {}", json); + fw.write(json); + } + return path; + } + + private String getJobConfig(CollectionTask task) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + Map parameter = objectMapper.readValue( + task.getConfig(), + new TypeReference<>() {} + ); + Map job = new HashMap<>(); + Map content = new HashMap<>(); + Map reader = new HashMap<>(); + reader.put("name", "nfsreader"); + reader.put("parameter", parameter); + content.put("reader", reader); + Map writer = new HashMap<>(); + writer.put("name", "nfswriter"); + writer.put("parameter", parameter); + content.put("writer", writer); + job.put("content", List.of(content)); + Map setting = new HashMap<>(); + Map channel = new HashMap<>(); + channel.put("channel", 2); + setting.put("speed", channel); + job.put("setting", setting); + Map jobConfig = new HashMap<>(); + jobConfig.put("job", job); + return objectMapper.writeValueAsString(jobConfig); + } catch (Exception e) { + log.error("Failed to parse task config", e); + throw new RuntimeException("Failed to parse task config", e); + } + } + + private String defaultJobJson() { + // 提供一个最小可运行的空 job,实际会被具体任务覆盖 + return "{\n \"job\": {\n \"setting\": {\n \"speed\": {\n \"channel\": 1\n }\n },\n \"content\": []\n }\n}"; + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProcessRunner.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProcessRunner.java new file mode 100644 index 0000000..fda00a8 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProcessRunner.java @@ -0,0 +1,46 @@ +package com.datamate.collection.infrastructure.runtime.datax; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.exec.*; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.time.Duration; + +@Slf4j +@Component +@RequiredArgsConstructor +public class DataxProcessRunner { + + private final DataxProperties props; + + public int runJob(File jobFile, String executionId, Duration timeout) throws Exception { + File logFile = new File(props.getLogPath(), String.format("datax-%s.log", executionId)); + String python = props.getPythonPath(); + String dataxPy = props.getHomePath() + File.separator + "bin" + File.separator + "datax.py"; + String cmd = String.format("%s %s %s", python, dataxPy, jobFile.getAbsolutePath()); + + log.info("Execute DataX: {}", cmd); + + CommandLine cl = CommandLine.parse(cmd); + DefaultExecutor executor = new DefaultExecutor(); + + // 将日志追加输出到文件 + File parent = logFile.getParentFile(); + if (!parent.exists()) parent.mkdirs(); + + ExecuteStreamHandler streamHandler = new PumpStreamHandler( + new org.apache.commons.io.output.TeeOutputStream( + new java.io.FileOutputStream(logFile, true), System.out), + new org.apache.commons.io.output.TeeOutputStream( + new java.io.FileOutputStream(logFile, true), System.err) + ); + executor.setStreamHandler(streamHandler); + + ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout.toMillis()); + executor.setWatchdog(watchdog); + + return executor.execute(cl); + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProperties.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProperties.java new file mode 100644 index 0000000..e194444 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/infrastructure/runtime/datax/DataxProperties.java @@ -0,0 +1,17 @@ +package com.datamate.collection.infrastructure.runtime.datax; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "datamate.data-collection.datax") +public class DataxProperties { + private String homePath; // DATAX_HOME + private String pythonPath; // python 可执行文件 + private String jobConfigPath; // 生成的作业文件目录 + private String logPath; // 运行日志目录 + private Integer maxMemory = 2048; + private Integer channelCount = 5; +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/converter/CollectionTaskConverter.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/converter/CollectionTaskConverter.java new file mode 100644 index 0000000..1cbdde3 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/converter/CollectionTaskConverter.java @@ -0,0 +1,52 @@ +package com.datamate.collection.interfaces.converter; + +import com.datamate.collection.domain.model.CollectionTask; +import com.datamate.collection.domain.model.DataxTemplate; +import com.datamate.collection.interfaces.dto.*; +import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.Map; + +@Mapper +public interface CollectionTaskConverter { + CollectionTaskConverter INSTANCE = Mappers.getMapper(CollectionTaskConverter.class); + + @Mapping(source = "config", target = "config", qualifiedByName = "parseJsonToMap") + CollectionTaskResponse toResponse(CollectionTask task); + + CollectionTaskSummary toSummary(CollectionTask task); + + DataxTemplateSummary toTemplateSummary(DataxTemplate template); + + @Mapping(source = "config", target = "config", qualifiedByName = "mapToJsonString") + CollectionTask toCollectionTask(CreateCollectionTaskRequest request); + + @Mapping(source = "config", target = "config", qualifiedByName = "mapToJsonString") + CollectionTask toCollectionTask(UpdateCollectionTaskRequest request); + + @Named("parseJsonToMap") + default Map parseJsonToMap(String json) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(json, Map.class); + } catch (Exception e) { + throw BusinessException.of(SystemErrorCode.INVALID_PARAMETER); + } + } + + @Named("mapToJsonString") + default String mapToJsonString(Map map) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(map != null ? map : Map.of()); + } catch (Exception e) { + throw BusinessException.of(SystemErrorCode.INVALID_PARAMETER); + } + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/CollectionTaskController.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/CollectionTaskController.java new file mode 100644 index 0000000..d9aa353 --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/CollectionTaskController.java @@ -0,0 +1,83 @@ +package com.datamate.collection.interfaces.rest; + +import com.datamate.collection.application.service.CollectionTaskService; +import com.datamate.collection.domain.model.CollectionTask; +import com.datamate.collection.domain.model.DataxTemplate; +import com.datamate.collection.interfaces.api.CollectionTaskApi; +import com.datamate.collection.interfaces.converter.CollectionTaskConverter; +import com.datamate.collection.interfaces.dto.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@RestController +@RequiredArgsConstructor +@Validated +public class CollectionTaskController implements CollectionTaskApi { + + private final CollectionTaskService taskService; + + @Override + public ResponseEntity createTask(CreateCollectionTaskRequest request) { + CollectionTask task = CollectionTaskConverter.INSTANCE.toCollectionTask(request); + task.setId(UUID.randomUUID().toString()); + task.addPath(); + return ResponseEntity.ok().body(CollectionTaskConverter.INSTANCE.toResponse(taskService.create(task))); + } + + @Override + public ResponseEntity updateTask(String id, UpdateCollectionTaskRequest request) { + if (taskService.get(id) == null) { + return ResponseEntity.notFound().build(); + } + CollectionTask task = CollectionTaskConverter.INSTANCE.toCollectionTask(request); + task.setId(id); + return ResponseEntity.ok(CollectionTaskConverter.INSTANCE.toResponse(taskService.update(task))); + } + + @Override + public ResponseEntity deleteTask(String id) { + taskService.delete(id); + return ResponseEntity.ok().build(); + } + + @Override + public ResponseEntity getTaskDetail(String id) { + CollectionTask task = taskService.get(id); + return task == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(CollectionTaskConverter.INSTANCE.toResponse(task)); + } + + @Override + public ResponseEntity getTasks(Integer page, Integer size, TaskStatus status, String name) { + var list = taskService.list(page, size, status == null ? null : status.getValue(), name); + PagedCollectionTaskSummary response = new PagedCollectionTaskSummary(); + response.setContent(list.stream().map(CollectionTaskConverter.INSTANCE::toSummary).collect(Collectors.toList())); + response.setNumber(page); + response.setSize(size); + response.setTotalElements(list.size()); // 简化处理,实际项目中应该有单独的count查询 + response.setTotalPages(size == null || size == 0 ? 1 : (int) Math.ceil(list.size() * 1.0 / size)); + return ResponseEntity.ok(response); + } + + @Override + public ResponseEntity templatesGet(String sourceType, String targetType, + Integer page, Integer size) { + int pageNum = page != null ? page : 0; + int pageSize = size != null ? size : 20; + List templates = taskService.listTemplates(sourceType, targetType, pageNum, pageSize); + int totalElements = taskService.countTemplates(sourceType, targetType); + PagedDataxTemplates response = new PagedDataxTemplates(); + response.setContent(templates.stream().map(CollectionTaskConverter.INSTANCE::toTemplateSummary).collect(Collectors.toList())); + response.setNumber(pageNum); + response.setSize(pageSize); + response.setTotalElements(totalElements); + response.setTotalPages(pageSize > 0 ? (int) Math.ceil(totalElements * 1.0 / pageSize) : 1); + return ResponseEntity.ok(response); + } +} diff --git a/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/TaskExecutionController.java b/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/TaskExecutionController.java new file mode 100644 index 0000000..6d392bb --- /dev/null +++ b/backend/services/data-collection-service/src/main/java/com/datamate/collection/interfaces/rest/TaskExecutionController.java @@ -0,0 +1,101 @@ +package com.datamate.collection.interfaces.rest; + +import com.datamate.collection.application.service.CollectionTaskService; +import com.datamate.collection.application.service.TaskExecutionService; +import com.datamate.collection.domain.model.TaskExecution; +import com.datamate.collection.interfaces.api.TaskExecutionApi; +import com.datamate.collection.interfaces.dto.PagedTaskExecutions; +import com.datamate.collection.interfaces.dto.TaskExecutionDetail; +import com.datamate.collection.interfaces.dto.TaskExecutionResponse; +import com.datamate.collection.interfaces.dto.TaskStatus; // DTO enum +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import java.util.stream.Collectors; + +@RestController +@RequiredArgsConstructor +@Validated +public class TaskExecutionController implements TaskExecutionApi { + + private final TaskExecutionService executionService; + private final CollectionTaskService taskService; + + private TaskExecutionDetail toDetail(TaskExecution e) { + TaskExecutionDetail d = new TaskExecutionDetail(); + d.setId(e.getId()); + d.setTaskId(e.getTaskId()); + d.setTaskName(e.getTaskName()); + if (e.getStatus() != null) { d.setStatus(TaskStatus.fromValue(e.getStatus().name())); } + d.setProgress(e.getProgress()); + d.setRecordsTotal(e.getRecordsTotal() != null ? e.getRecordsTotal().intValue() : null); + d.setRecordsProcessed(e.getRecordsProcessed() != null ? e.getRecordsProcessed().intValue() : null); + d.setRecordsSuccess(e.getRecordsSuccess() != null ? e.getRecordsSuccess().intValue() : null); + d.setRecordsFailed(e.getRecordsFailed() != null ? e.getRecordsFailed().intValue() : null); + d.setThroughput(e.getThroughput()); + d.setDataSizeBytes(e.getDataSizeBytes() != null ? e.getDataSizeBytes().intValue() : null); + d.setStartedAt(e.getStartedAt()); + d.setCompletedAt(e.getCompletedAt()); + d.setDurationSeconds(e.getDurationSeconds()); + d.setErrorMessage(e.getErrorMessage()); + return d; + } + + // GET /executions/{id} + @Override + public ResponseEntity executionsIdGet(String id) { + var exec = executionService.get(id); + return exec == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(toDetail(exec)); + } + + // DELETE /executions/{id} + @Override + public ResponseEntity executionsIdDelete(String id) { + executionService.stop(id); // 幂等处理,在service内部判断状态 + return ResponseEntity.noContent().build(); + } + + // POST /tasks/{id}/execute -> 201 + @Override + public ResponseEntity tasksIdExecutePost(String id) { + var task = taskService.get(id); + if (task == null) { return ResponseEntity.notFound().build(); } + var latestExec = executionService.getLatestByTaskId(id); + if (latestExec != null && latestExec.getStatus() == com.datamate.collection.domain.model.TaskStatus.RUNNING) { + TaskExecutionResponse r = new TaskExecutionResponse(); + r.setId(latestExec.getId()); + r.setTaskId(latestExec.getTaskId()); + r.setTaskName(latestExec.getTaskName()); + r.setStatus(TaskStatus.fromValue(latestExec.getStatus().name())); + r.setStartedAt(latestExec.getStartedAt()); + return ResponseEntity.status(HttpStatus.CREATED).body(r); // 返回已有运行实例 + } + var exec = taskService.startExecution(task); + TaskExecutionResponse r = new TaskExecutionResponse(); + r.setId(exec.getId()); + r.setTaskId(exec.getTaskId()); + r.setTaskName(exec.getTaskName()); + r.setStatus(TaskStatus.fromValue(exec.getStatus().name())); + r.setStartedAt(exec.getStartedAt()); + return ResponseEntity.status(HttpStatus.CREATED).body(r); + } + + // GET /tasks/{id}/executions -> 分页 + @Override + public ResponseEntity tasksIdExecutionsGet(String id, Integer page, Integer size) { + if (page == null || page < 0) { page = 0; } + if (size == null || size <= 0) { size = 20; } + var list = executionService.list(id, null, null, null, page, size); + long total = executionService.count(id, null, null, null); + PagedTaskExecutions p = new PagedTaskExecutions(); + p.setContent(list.stream().map(this::toDetail).collect(Collectors.toList())); + p.setNumber(page); + p.setSize(size); + p.setTotalElements((int) total); + p.setTotalPages(size == 0 ? 1 : (int) Math.ceil(total * 1.0 / size)); + return ResponseEntity.ok(p); + } +} diff --git a/backend/services/data-collection-service/src/main/resources/config/application-datacollection.yml b/backend/services/data-collection-service/src/main/resources/config/application-datacollection.yml new file mode 100644 index 0000000..b4b6fee --- /dev/null +++ b/backend/services/data-collection-service/src/main/resources/config/application-datacollection.yml @@ -0,0 +1,23 @@ +datamate: + data-collection: + # DataX配置 + datax: + home-path: ${DATAX_HOME:D:/datax} + python-path: ${DATAX_PYTHON_PATH:python3} + job-config-path: ${DATAX_JOB_PATH:./data/temp/datax/jobs} + log-path: ${DATAX_LOG_PATH:./logs/datax} + max-memory: ${DATAX_MAX_MEMORY:2048} + channel-count: ${DATAX_CHANNEL_COUNT:5} + + # 执行配置 + execution: + max-concurrent-tasks: ${DATA_COLLECTION_MAX_CONCURRENT_TASKS:10} + task-timeout-minutes: ${DATA_COLLECTION_TASK_TIMEOUT:120} + retry-count: ${DATA_COLLECTION_RETRY_COUNT:3} + retry-interval-seconds: ${DATA_COLLECTION_RETRY_INTERVAL:30} + + # 监控配置 + monitoring: + status-check-interval-seconds: ${DATA_COLLECTION_STATUS_CHECK_INTERVAL:30} + log-retention-days: ${DATA_COLLECTION_LOG_RETENTION:30} + enable-metrics: ${DATA_COLLECTION_ENABLE_METRICS:true} diff --git a/backend/services/data-collection-service/src/main/resources/mappers/CollectionTaskMapper.xml b/backend/services/data-collection-service/src/main/resources/mappers/CollectionTaskMapper.xml new file mode 100644 index 0000000..3a195a2 --- /dev/null +++ b/backend/services/data-collection-service/src/main/resources/mappers/CollectionTaskMapper.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, + name, description, config, status, sync_mode, + schedule_expression, retry_count, timeout_seconds, max_records, sort_field, + last_execution_id, created_at, updated_at, created_by, updated_by + + + + + id, name, source_type, target_type, template_content, description, version, is_system, created_at, updated_at, created_by + + + + + INSERT INTO t_dc_collection_tasks (id, name, description, config, status, sync_mode, + schedule_expression, retry_count, timeout_seconds, max_records, sort_field, + last_execution_id, created_at, updated_at, created_by, updated_by) + VALUES (#{id}, #{name}, #{description}, #{config}, #{status}, #{syncMode}, + #{scheduleExpression}, #{retryCount}, #{timeoutSeconds}, #{maxRecords}, #{sortField}, + #{lastExecutionId}, #{createdAt}, #{updatedAt}, #{createdBy}, #{updatedBy}) + + + + + UPDATE t_dc_collection_tasks + SET name = #{name}, + description = #{description}, + config = #{config}, + status = #{status}, + sync_mode = #{syncMode}, + schedule_expression = #{scheduleExpression}, + retry_count = #{retryCount}, + timeout_seconds = #{timeoutSeconds}, + max_records = #{maxRecords}, + sort_field = #{sortField}, + last_execution_id = #{lastExecutionId}, + updated_at = #{updatedAt}, + updated_by = #{updatedBy} + WHERE id = #{id} + + + + + DELETE FROM t_dc_collection_tasks WHERE id = #{id} + + + + + + + + + + + + + + + + + + + + UPDATE t_dc_collection_tasks SET status = #{status}, updated_at = NOW() WHERE id = #{id} + + + + + UPDATE t_dc_collection_tasks SET last_execution_id = #{lastExecutionId}, updated_at = NOW() WHERE id = #{id} + + + + + + + + + + + + diff --git a/backend/services/data-collection-service/src/main/resources/mappers/TaskExecutionMapper.xml b/backend/services/data-collection-service/src/main/resources/mappers/TaskExecutionMapper.xml new file mode 100644 index 0000000..6b6d0a0 --- /dev/null +++ b/backend/services/data-collection-service/src/main/resources/mappers/TaskExecutionMapper.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, task_id, task_name, status, progress, records_total, records_processed, + records_success, records_failed, throughput, data_size_bytes, started_at, + completed_at, duration_seconds, error_message, datax_job_id, config, result, created_at + + + + + INSERT INTO t_dc_task_executions ( + id, task_id, task_name, status, progress, records_total, records_processed, + records_success, records_failed, throughput, data_size_bytes, started_at, + completed_at, duration_seconds, error_message, datax_job_id, config, result, created_at + ) VALUES ( + #{id}, #{taskId}, #{taskName}, #{status}, #{progress}, #{recordsTotal}, #{recordsProcessed}, + #{recordsSuccess}, #{recordsFailed}, #{throughput}, #{dataSizeBytes}, #{startedAt}, + #{completedAt}, #{durationSeconds}, #{errorMessage}, #{dataxJobId}, #{config}, #{result}, #{createdAt} + ) + + + + + UPDATE t_dc_task_executions + SET status = #{status}, + progress = #{progress}, + records_total = #{recordsTotal}, + records_processed = #{recordsProcessed}, + records_success = #{recordsSuccess}, + records_failed = #{recordsFailed}, + throughput = #{throughput}, + data_size_bytes = #{dataSizeBytes}, + completed_at = #{completedAt}, + duration_seconds = #{durationSeconds}, + error_message = #{errorMessage}, + result = #{result} + WHERE id = #{id} + + + + + DELETE FROM t_dc_task_executions WHERE id = #{id} + + + + + + + + + + + + + + + + + + + + UPDATE t_dc_task_executions + SET status = #{status}, + progress = #{progress}, + records_processed = #{recordsProcessed}, + throughput = #{throughput} + WHERE id = #{id} + + + + + UPDATE t_dc_task_executions + SET status = #{status}, + progress = 100.00, + completed_at = #{completedAt}, + duration_seconds = #{durationSeconds}, + records_success = #{recordsSuccess}, + records_failed = #{recordsFailed}, + data_size_bytes = #{dataSizeBytes}, + error_message = #{errorMessage}, + result = #{result} + WHERE id = #{id} + + + + + + + + + + + DELETE FROM t_dc_task_executions + WHERE started_at < #{beforeDate} + + + diff --git a/backend/services/data-evaluation-service/pom.xml b/backend/services/data-evaluation-service/pom.xml new file mode 100644 index 0000000..c976d19 --- /dev/null +++ b/backend/services/data-evaluation-service/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + data-evaluation-service + Data Evaluation Service + 数据评估服务 + + + + com.datamate + domain-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + mysql + mysql-connector-java + ${mysql.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.openapitools + jackson-databind-nullable + + + jakarta.validation + jakarta.validation-api + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + 6.6.0 + + + + generate + + + ${project.basedir}/../../openapi/specs/data-evaluation.yaml + spring + ${project.build.directory}/generated-sources/openapi + com.datamate.evaluation.interfaces.api + com.datamate.evaluation.interfaces.dto + + true + true + true + springdoc + + + + + + + + + + diff --git a/backend/services/data-management-service/pom.xml b/backend/services/data-management-service/pom.xml new file mode 100644 index 0000000..e6f0c16 --- /dev/null +++ b/backend/services/data-management-service/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + data-management-service + Data Management Service + 数据管理服务 + + + + com.datamate + domain-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + org.springframework.boot + spring-boot-starter-data-redis + + + mysql + mysql-connector-java + ${mysql.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.openapitools + jackson-databind-nullable + + + jakarta.validation + jakarta.validation-api + + + org.springframework.data + spring-data-commons + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + exec + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + ${lombok-mapstruct-binding.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + -parameters + -Amapstruct.defaultComponentModel=spring + + + + + + + diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/DataManagementServiceConfiguration.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/DataManagementServiceConfiguration.java new file mode 100644 index 0000000..8e2b586 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/DataManagementServiceConfiguration.java @@ -0,0 +1,22 @@ +package com.datamate.datamanagement; + +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * Data Management Service Configuration + * 数据管理服务配置类 - 多源接入、元数据、血缘治理 + */ +@Configuration +@EnableFeignClients(basePackages = "com.datamate.datamanagement.infrastructure.client") +@EnableAsync +@ComponentScan(basePackages = { + "com.datamate.datamanagement", + "com.datamate.shared" +}) +public class DataManagementServiceConfiguration { + // Service configuration class for JAR packaging + // 作为jar包形式提供服务的配置类 +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java new file mode 100644 index 0000000..fa6b07a --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetApplicationService.java @@ -0,0 +1,288 @@ +package com.datamate.datamanagement.application; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.datamate.datamanagement.interfaces.dto.*; +import com.datamate.common.infrastructure.exception.BusinessAssert; +import com.datamate.common.interfaces.PagedResponse; +import com.datamate.datamanagement.domain.model.dataset.Dataset; +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import com.datamate.datamanagement.domain.model.dataset.Tag; +import com.datamate.datamanagement.infrastructure.client.CollectionTaskClient; +import com.datamate.datamanagement.infrastructure.client.dto.CollectionTaskDetailResponse; +import com.datamate.datamanagement.infrastructure.client.dto.LocalCollectionConfig; +import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode; +import com.datamate.datamanagement.infrastructure.persistence.mapper.TagMapper; +import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetFileRepository; +import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetRepository; +import com.datamate.datamanagement.interfaces.converter.DatasetConverter; +import com.datamate.datamanagement.interfaces.dto.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 数据集应用服务(对齐 DB schema,使用 UUID 字符串主键) + */ +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class DatasetApplicationService { + private final DatasetRepository datasetRepository; + private final TagMapper tagMapper; + private final DatasetFileRepository datasetFileRepository; + private final CollectionTaskClient collectionTaskClient; + private final FileMetadataService fileMetadataService; + private final ObjectMapper objectMapper; + + @Value("${dataset.base.path:/dataset}") + private String datasetBasePath; + + /** + * 创建数据集 + */ + @Transactional + public Dataset createDataset(CreateDatasetRequest createDatasetRequest) { + BusinessAssert.isTrue(datasetRepository.findByName(createDatasetRequest.getName()) == null, DataManagementErrorCode.DATASET_ALREADY_EXISTS); + // 创建数据集对象 + Dataset dataset = DatasetConverter.INSTANCE.convertToDataset(createDatasetRequest); + dataset.initCreateParam(datasetBasePath); + // 处理标签 + Set processedTags = Optional.ofNullable(createDatasetRequest.getTags()) + .filter(CollectionUtils::isNotEmpty) + .map(this::processTagNames) + .orElseGet(HashSet::new); + dataset.setTags(processedTags); + datasetRepository.save(dataset); + + //todo 需要解耦这块逻辑 + if (StringUtils.hasText(createDatasetRequest.getDataSource())) { + // 数据源id不为空,使用异步线程进行文件扫盘落库 + processDataSourceAsync(dataset.getId(), createDatasetRequest.getDataSource()); + } + return dataset; + } + + public Dataset updateDataset(String datasetId, UpdateDatasetRequest updateDatasetRequest) { + Dataset dataset = datasetRepository.getById(datasetId); + BusinessAssert.notNull(dataset, DataManagementErrorCode.DATASET_NOT_FOUND); + if (StringUtils.hasText(updateDatasetRequest.getName())) { + dataset.setName(updateDatasetRequest.getName()); + } + if (StringUtils.hasText(updateDatasetRequest.getDescription())) { + dataset.setDescription(updateDatasetRequest.getDescription()); + } + if (CollectionUtils.isNotEmpty(updateDatasetRequest.getTags())) { + dataset.setTags(processTagNames(updateDatasetRequest.getTags())); + } + if (Objects.nonNull(updateDatasetRequest.getStatus())) { + dataset.setStatus(updateDatasetRequest.getStatus()); + } + if (StringUtils.hasText(updateDatasetRequest.getDataSource())) { + // 数据源id不为空,使用异步线程进行文件扫盘落库 + processDataSourceAsync(dataset.getId(), updateDatasetRequest.getDataSource()); + } + datasetRepository.updateById(dataset); + return dataset; + } + + /** + * 删除数据集 + */ + public void deleteDataset(String datasetId) { + datasetRepository.removeById(datasetId); + } + + /** + * 获取数据集详情 + */ + @Transactional(readOnly = true) + public Dataset getDataset(String datasetId) { + Dataset dataset = datasetRepository.getById(datasetId); + BusinessAssert.notNull(dataset, DataManagementErrorCode.DATASET_NOT_FOUND); + return dataset; + } + + /** + * 分页查询数据集 + */ + @Transactional(readOnly = true) + public PagedResponse getDatasets(DatasetPagingQuery query) { + IPage page = new Page<>(query.getPage(), query.getSize()); + page = datasetRepository.findByCriteria(page, query); + return PagedResponse.of(DatasetConverter.INSTANCE.convertToResponse(page.getRecords()), page.getCurrent(), page.getTotal(), page.getPages()); + } + + /** + * 处理标签名称,创建或获取标签 + */ + private Set processTagNames(List tagNames) { + Set tags = new HashSet<>(); + for (String tagName : tagNames) { + Tag tag = tagMapper.findByName(tagName); + if (tag == null) { + Tag newTag = new Tag(tagName, null, null, "#007bff"); + newTag.setUsageCount(0L); + newTag.setId(UUID.randomUUID().toString()); + tagMapper.insert(newTag); + tag = newTag; + } + tag.setUsageCount(tag.getUsageCount() == null ? 1L : tag.getUsageCount() + 1); + tagMapper.updateUsageCount(tag.getId(), tag.getUsageCount()); + tags.add(tag); + } + return tags; + } + + /** + * 获取数据集统计信息 + */ + @Transactional(readOnly = true) + public Map getDatasetStatistics(String datasetId) { + Dataset dataset = datasetRepository.getById(datasetId); + if (dataset == null) { + throw new IllegalArgumentException("Dataset not found: " + datasetId); + } + + Map statistics = new HashMap<>(); + + // 基础统计 + Long totalFiles = datasetFileRepository.countByDatasetId(datasetId); + Long completedFiles = datasetFileRepository.countCompletedByDatasetId(datasetId); + Long totalSize = datasetFileRepository.sumSizeByDatasetId(datasetId); + + statistics.put("totalFiles", totalFiles != null ? totalFiles.intValue() : 0); + statistics.put("completedFiles", completedFiles != null ? completedFiles.intValue() : 0); + statistics.put("totalSize", totalSize != null ? totalSize : 0L); + + // 完成率计算 + float completionRate = 0.0f; + if (totalFiles != null && totalFiles > 0) { + completionRate = (completedFiles != null ? completedFiles.floatValue() : 0.0f) / totalFiles.floatValue() * 100.0f; + } + statistics.put("completionRate", completionRate); + + // 文件类型分布统计 + Map fileTypeDistribution = new HashMap<>(); + List allFiles = datasetFileRepository.findAllByDatasetId(datasetId); + if (allFiles != null) { + for (DatasetFile file : allFiles) { + String fileType = file.getFileType() != null ? file.getFileType() : "unknown"; + fileTypeDistribution.put(fileType, fileTypeDistribution.getOrDefault(fileType, 0) + 1); + } + } + statistics.put("fileTypeDistribution", fileTypeDistribution); + + // 状态分布统计 + Map statusDistribution = new HashMap<>(); + if (allFiles != null) { + for (DatasetFile file : allFiles) { + String status = file.getStatus() != null ? file.getStatus() : "unknown"; + statusDistribution.put(status, statusDistribution.getOrDefault(status, 0) + 1); + } + } + statistics.put("statusDistribution", statusDistribution); + + return statistics; + } + + /** + * 获取所有数据集的汇总统计信息 + */ + public AllDatasetStatisticsResponse getAllDatasetStatistics() { + return datasetRepository.getAllDatasetStatistics(); + } + + /** + * 异步处理数据源文件扫描 + * + * @param datasetId 数据集ID + * @param dataSourceId 数据源ID(归集任务ID) + */ + @Async + public void processDataSourceAsync(String datasetId, String dataSourceId) { + try { + log.info("开始处理数据源文件扫描,数据集ID: {}, 数据源ID: {}", datasetId, dataSourceId); + + // 1. 调用数据归集服务获取任务详情 + CollectionTaskDetailResponse taskDetail = collectionTaskClient.getTaskDetail(dataSourceId).getData(); + if (taskDetail == null) { + log.error("获取归集任务详情失败,任务ID: {}", dataSourceId); + return; + } + + log.info("获取到归集任务详情: {}", taskDetail); + + // 2. 解析任务配置 + LocalCollectionConfig config = parseTaskConfig(taskDetail.getConfig()); + if (config == null) { + log.error("解析任务配置失败,任务ID: {}", dataSourceId); + return; + } + + // 4. 获取文件路径列表 + List filePaths = config.getFilePaths(); + if (CollectionUtils.isEmpty(filePaths)) { + log.warn("文件路径列表为空,任务ID: {}", dataSourceId); + return; + } + + log.info("开始扫描文件,共 {} 个文件路径", filePaths.size()); + + // 5. 扫描文件元数据 + List datasetFiles = fileMetadataService.scanFiles(filePaths, datasetId); + // 查询数据集中已存在的文件 + List existDatasetFileList = datasetFileRepository.findAllByDatasetId(datasetId); + Map existDatasetFilePathMap = existDatasetFileList.stream().collect(Collectors.toMap(DatasetFile::getFilePath, Function.identity())); + Dataset dataset = datasetRepository.getById(datasetId); + + // 6. 批量插入数据集文件表 + if (CollectionUtils.isNotEmpty(datasetFiles)) { + for (DatasetFile datasetFile : datasetFiles) { + if (existDatasetFilePathMap.containsKey(datasetFile.getFilePath())) { + DatasetFile existDatasetFile = existDatasetFilePathMap.get(datasetFile.getFilePath()); + dataset.removeFile(existDatasetFile); + existDatasetFile.setFileSize(datasetFile.getFileSize()); + dataset.addFile(existDatasetFile); + datasetFileRepository.updateById(existDatasetFile); + } else { + dataset.addFile(datasetFile); + datasetFileRepository.save(datasetFile); + } + } + log.info("文件元数据写入完成,共写入 {} 条记录", datasetFiles.size()); + } else { + log.warn("未扫描到有效文件"); + } + datasetRepository.updateById(dataset); + } catch (Exception e) { + log.error("处理数据源文件扫描失败,数据集ID: {}, 数据源ID: {}", datasetId, dataSourceId, e); + } + } + + /** + * 解析任务配置 + */ + private LocalCollectionConfig parseTaskConfig(Map configMap) { + try { + if (configMap == null || configMap.isEmpty()) { + return null; + } + return objectMapper.convertValue(configMap, LocalCollectionConfig.class); + } catch (Exception e) { + log.error("解析任务配置失败", e); + return null; + } + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java new file mode 100644 index 0000000..eadecc0 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java @@ -0,0 +1,306 @@ +package com.datamate.datamanagement.application; + +import com.datamate.common.domain.model.ChunkUploadPreRequest; +import com.datamate.common.domain.model.FileUploadResult; +import com.datamate.common.domain.service.FileService; +import com.datamate.common.domain.utils.AnalyzerUtils; +import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.datamate.datamanagement.domain.contants.DatasetConstant; +import com.datamate.datamanagement.domain.model.dataset.Dataset; +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import com.datamate.datamanagement.domain.model.dataset.DatasetFileUploadCheckInfo; +import com.datamate.datamanagement.domain.model.dataset.StatusConstants; +import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetFileRepository; +import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetRepository; +import com.datamate.datamanagement.interfaces.converter.DatasetConverter; +import com.datamate.datamanagement.interfaces.dto.UploadFileRequest; +import com.datamate.datamanagement.interfaces.dto.UploadFilesPreRequest; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.RowBounds; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 数据集文件应用服务 + */ +@Slf4j +@Service +@Transactional +public class DatasetFileApplicationService { + + private final DatasetFileRepository datasetFileRepository; + private final DatasetRepository datasetRepository; + private final Path fileStorageLocation; + private final FileService fileService; + + @Value("${dataset.base.path:/dataset}") + private String datasetBasePath; + + @Autowired + public DatasetFileApplicationService(DatasetFileRepository datasetFileRepository, + DatasetRepository datasetRepository, FileService fileService, + @Value("${app.file.upload-dir:./dataset}") String uploadDir) { + this.datasetFileRepository = datasetFileRepository; + this.datasetRepository = datasetRepository; + this.fileStorageLocation = Paths.get(uploadDir).toAbsolutePath().normalize(); + this.fileService = fileService; + try { + Files.createDirectories(this.fileStorageLocation); + } catch (Exception ex) { + throw new RuntimeException("Could not create the directory where the uploaded files will be stored.", ex); + } + } + + /** + * 上传文件到数据集 + */ + public DatasetFile uploadFile(String datasetId, MultipartFile file) { + Dataset dataset = datasetRepository.getById(datasetId); + if (dataset == null) { + throw new IllegalArgumentException("Dataset not found: " + datasetId); + } + + String originalFilename = file.getOriginalFilename(); + String fileName = originalFilename != null ? originalFilename : "file"; + try { + // 保存文件到磁盘 + Path targetLocation = this.fileStorageLocation.resolve(datasetId + File.separator + fileName); + // 确保目标目录存在 + Files.createDirectories(targetLocation); + Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING); + + // 创建文件实体(UUID 主键) + DatasetFile datasetFile = new DatasetFile(); + datasetFile.setId(UUID.randomUUID().toString()); + datasetFile.setDatasetId(datasetId); + datasetFile.setFileName(fileName); + datasetFile.setFilePath(targetLocation.toString()); + datasetFile.setFileType(getFileExtension(originalFilename)); + datasetFile.setFileSize(file.getSize()); + datasetFile.setUploadTime(LocalDateTime.now()); + datasetFile.setStatus(StatusConstants.DatasetFileStatuses.COMPLETED); + + // 保存到数据库 + datasetFileRepository.save(datasetFile); + + // 更新数据集统计 + dataset.addFile(datasetFile); + datasetRepository.updateById(dataset); + + return datasetFileRepository.findByDatasetIdAndFileName(datasetId, fileName); + + } catch (IOException ex) { + log.error("Could not store file {}", fileName, ex); + throw new RuntimeException("Could not store file " + fileName, ex); + } + } + + /** + * 获取数据集文件列表 + */ + @Transactional(readOnly = true) + public Page getDatasetFiles(String datasetId, String fileType, + String status, Pageable pageable) { + RowBounds bounds = new RowBounds(pageable.getPageNumber() * pageable.getPageSize(), pageable.getPageSize()); + List content = datasetFileRepository.findByCriteria(datasetId, fileType, status, bounds); + long total = content.size() < pageable.getPageSize() && pageable.getPageNumber() == 0 ? content.size() : content.size() + (long) pageable.getPageNumber() * pageable.getPageSize(); + return new PageImpl<>(content, pageable, total); + } + + /** + * 获取文件详情 + */ + @Transactional(readOnly = true) + public DatasetFile getDatasetFile(String datasetId, String fileId) { + DatasetFile file = datasetFileRepository.getById(fileId); + if (file == null) { + throw new IllegalArgumentException("File not found: " + fileId); + } + if (!file.getDatasetId().equals(datasetId)) { + throw new IllegalArgumentException("File does not belong to the specified dataset"); + } + return file; + } + + /** + * 删除文件 + */ + public void deleteDatasetFile(String datasetId, String fileId) { + DatasetFile file = getDatasetFile(datasetId, fileId); + try { + Path filePath = Paths.get(file.getFilePath()); + Files.deleteIfExists(filePath); + } catch (IOException ex) { + // ignore + } + datasetFileRepository.removeById(fileId); + + Dataset dataset = datasetRepository.getById(datasetId); + // 简单刷新统计(精确处理可从DB统计) + dataset.setFileCount(Math.max(0, dataset.getFileCount() - 1)); + dataset.setSizeBytes(Math.max(0, dataset.getSizeBytes() - (file.getFileSize() != null ? file.getFileSize() : 0))); + datasetRepository.updateById(dataset); + } + + /** + * 下载文件 + */ + @Transactional(readOnly = true) + public Resource downloadFile(String datasetId, String fileId) { + DatasetFile file = getDatasetFile(datasetId, fileId); + try { + Path filePath = Paths.get(file.getFilePath()).normalize(); + Resource resource = new UrlResource(filePath.toUri()); + if (resource.exists()) { + return resource; + } else { + throw new RuntimeException("File not found: " + file.getFileName()); + } + } catch (MalformedURLException ex) { + throw new RuntimeException("File not found: " + file.getFileName(), ex); + } + } + + /** + * 下载文件 + */ + @Transactional(readOnly = true) + public void downloadDatasetFileAsZip(String datasetId, HttpServletResponse response) { + List allByDatasetId = datasetFileRepository.findAllByDatasetId(datasetId); + response.setContentType("application/zip"); + String zipName = String.format("dataset_%s.zip", + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + zipName); + try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) { + for (DatasetFile file : allByDatasetId) { + addToZipFile(file, zos); + } + } catch (IOException e) { + log.error("Failed to download files in batches.", e); + throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR); + } + } + + private void addToZipFile(DatasetFile file, ZipOutputStream zos) throws IOException { + if (file.getFilePath() == null || !Files.exists(Paths.get(file.getFilePath()))) { + log.warn("The file hasn't been found on filesystem, id: {}", file.getId()); + return; + } + try (InputStream fis = Files.newInputStream(Paths.get(file.getFilePath())); + BufferedInputStream bis = new BufferedInputStream(fis)) { + ZipEntry zipEntry = new ZipEntry(file.getFileName()); + zos.putNextEntry(zipEntry); + byte[] buffer = new byte[8192]; + int length; + while ((length = bis.read(buffer)) >= 0) { + zos.write(buffer, 0, length); + } + zos.closeEntry(); + } + } + + private String getFileExtension(String fileName) { + if (fileName == null || fileName.isEmpty()) { + return null; + } + int lastDotIndex = fileName.lastIndexOf("."); + if (lastDotIndex == -1) { + return null; + } + return fileName.substring(lastDotIndex + 1); + } + + /** + * 预上传 + * + * @param chunkUploadRequest 上传请求 + * @param datasetId 数据集id + * @return 请求id + */ + @Transactional + public String preUpload(UploadFilesPreRequest chunkUploadRequest, String datasetId) { + ChunkUploadPreRequest request = ChunkUploadPreRequest.builder().build(); + request.setUploadPath(datasetBasePath + File.separator + datasetId); + request.setTotalFileNum(chunkUploadRequest.getTotalFileNum()); + request.setServiceId(DatasetConstant.SERVICE_ID); + DatasetFileUploadCheckInfo checkInfo = new DatasetFileUploadCheckInfo(); + checkInfo.setDatasetId(datasetId); + checkInfo.setHasArchive(chunkUploadRequest.isHasArchive()); + try { + ObjectMapper objectMapper = new ObjectMapper(); + String checkInfoJson = objectMapper.writeValueAsString(checkInfo); + request.setCheckInfo(checkInfoJson); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Failed to serialize checkInfo to JSON", e); + } + return fileService.preUpload(request); + } + + /** + * 切片上传 + * + * @param uploadFileRequest 上传请求 + */ + @Transactional + public void chunkUpload(String datasetId, UploadFileRequest uploadFileRequest) { + FileUploadResult uploadResult = fileService.chunkUpload(DatasetConverter.INSTANCE.toChunkUploadRequest(uploadFileRequest)); + saveFileInfoToDb(uploadResult, uploadFileRequest, datasetId); + if (uploadResult.isAllFilesUploaded()) { + // 解析文件,后续依据需求看是否添加校验文件元数据和解析半结构化文件的逻辑, + } + } + + private void saveFileInfoToDb(FileUploadResult fileUploadResult, UploadFileRequest uploadFile, String datasetId) { + if (Objects.isNull(fileUploadResult.getSavedFile())) { + // 文件切片上传没有完成 + return; + } + Dataset dataset = datasetRepository.getById(datasetId); + File savedFile = fileUploadResult.getSavedFile(); + LocalDateTime currentTime = LocalDateTime.now(); + DatasetFile datasetFile = DatasetFile.builder() + .id(UUID.randomUUID().toString()) + .datasetId(datasetId) + .fileSize(savedFile.length()) + .uploadTime(currentTime) + .lastAccessTime(currentTime) + .fileName(uploadFile.getFileName()) + .filePath(savedFile.getPath()) + .fileType(AnalyzerUtils.getExtension(uploadFile.getFileName())) + .build(); + + datasetFileRepository.save(datasetFile); + dataset.addFile(datasetFile); + datasetRepository.updateById(dataset); + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/FileMetadataService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/FileMetadataService.java new file mode 100644 index 0000000..af5799c --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/FileMetadataService.java @@ -0,0 +1,127 @@ +package com.datamate.datamanagement.application; + +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * 文件元数据扫描服务 + */ +@Slf4j +@Service +public class FileMetadataService { + + /** + * 扫描文件路径列表,提取文件元数据 + * @param datasetId 数据集ID + * @return 数据集文件列表 + */ + public List scanFiles(List filePaths, String datasetId) { + List datasetFiles = new ArrayList<>(); + + if (filePaths == null || filePaths.isEmpty()) { + log.warn("文件路径列表为空,跳过扫描"); + return datasetFiles; + } + + for (String filePath : filePaths) { + try { + Path path = Paths.get(filePath); + + if (!Files.exists(path)) { + log.warn("路径不存在: {}", filePath); + continue; + } + + if (Files.isDirectory(path)) { + scanDirectory(datasetId, filePath, path, datasetFiles); + } else { + // 如果是文件,直接处理 + DatasetFile datasetFile = extractFileMetadata(filePath, datasetId); + if (datasetFile != null) { + datasetFiles.add(datasetFile); + } + } + } catch (Exception e) { + log.error("扫描路径失败: {}, 错误: {}", filePath, e.getMessage(), e); + } + } + + log.info("文件扫描完成,共扫描 {} 个文件", datasetFiles.size()); + return datasetFiles; + } + + private void scanDirectory(String datasetId, String filePath, Path path, + List datasetFiles) throws IOException { + // 如果是目录,扫描该目录下的所有文件(非递归) + List filesInDir = Files.list(path) + .filter(Files::isRegularFile) + .toList(); + + for (Path file : filesInDir) { + try { + DatasetFile datasetFile = extractFileMetadata(file.toString(), datasetId); + if (datasetFile != null) { + datasetFiles.add(datasetFile); + } + } catch (Exception e) { + log.error("处理目录中的文件失败: {}, 错误: {}", file, e.getMessage(), e); + } + } + log.info("已扫描目录 {} 下的 {} 个文件", filePath, filesInDir.size()); + } + /** + * @param filePath 文件路径 + * @param datasetId 数据集ID + * @return 数据集文件对象 + */ + private DatasetFile extractFileMetadata(String filePath, String datasetId) throws IOException { + Path path = Paths.get(filePath); + + if (!Files.exists(path)) { + log.warn("文件不存在: {}", filePath); + return null; + } + + if (!Files.isRegularFile(path)) { + log.warn("路径不是文件: {}", filePath); + return null; + } + + String fileName = path.getFileName().toString(); + long fileSize = Files.size(path); + String fileType = getFileExtension(fileName); + + return DatasetFile.builder() + .id(UUID.randomUUID().toString()) + .datasetId(datasetId) + .fileName(fileName) + .filePath(filePath) + .fileSize(fileSize) + .fileType(fileType) + .uploadTime(LocalDateTime.now()) + .lastAccessTime(LocalDateTime.now()) + .status("UPLOADED") + .build(); + } + + /** + * 获取文件扩展名 + */ + private String getFileExtension(String fileName) { + int lastDotIndex = fileName.lastIndexOf('.'); + if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) { + return fileName.substring(lastDotIndex + 1).toLowerCase(); + } + return "unknown"; + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/TagApplicationService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/TagApplicationService.java new file mode 100644 index 0000000..9558756 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/TagApplicationService.java @@ -0,0 +1,116 @@ +package com.datamate.datamanagement.application; + +import com.datamate.datamanagement.domain.model.dataset.Tag; +import com.datamate.datamanagement.infrastructure.persistence.mapper.TagMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.UUID; + +/** + * 标签应用服务(UUID 主键) + */ +@Service +@Transactional +public class TagApplicationService { + + private final TagMapper tagMapper; + + @Autowired + public TagApplicationService(TagMapper tagMapper) { + this.tagMapper = tagMapper; + } + + /** + * 创建标签 + */ + public Tag createTag(String name, String color, String description) { + // 检查名称是否已存在 + if (tagMapper.findByName(name) != null) { + throw new IllegalArgumentException("Tag with name '" + name + "' already exists"); + } + + Tag tag = new Tag(name, description, null, color); + tag.setUsageCount(0L); + tag.setId(UUID.randomUUID().toString()); + tagMapper.insert(tag); + return tagMapper.findById(tag.getId()); + } + + /** + * 更新标签 + * + * @param tag 待更新的标签实体,必须包含有效的 ID + * @return 更新结果 + */ + @Transactional + public Tag updateTag(Tag tag) { + Tag existingTag = tagMapper.findById(tag.getId()); + if (existingTag == null) { + throw new IllegalArgumentException("Tag not found: " + tag.getId()); + } + existingTag.setName(tag.getName()); + existingTag.setColor(tag.getColor()); + existingTag.setDescription(tag.getDescription()); + tagMapper.update(existingTag); + return tagMapper.findById(existingTag.getId()); + } + + @Transactional + public void deleteTag(List tagIds) { + List tags = tagMapper.findByIdIn(tagIds); + if (tags.stream().anyMatch(tag -> tag.getUsageCount() > 0)) { + throw new IllegalArgumentException("Cannot delete tags that are in use"); + } + if (CollectionUtils.isEmpty(tags)) { + return; + } + tagMapper.deleteTagsById(tags.stream().map(Tag::getId).toList()); + } + + /** + * 获取所有标签 + */ + @Transactional(readOnly = true) + public List getAllTags() { + return tagMapper.findAllByOrderByUsageCountDesc(); + } + + /** + * 根据关键词搜索标签 + */ + @Transactional(readOnly = true) + public List searchTags(String keyword) { + if (keyword == null || keyword.trim().isEmpty()) { + return getAllTags(); + } + return tagMapper.findByKeyword(keyword.trim()); + } + + /** + * 获取标签详情 + */ + @Transactional(readOnly = true) + public Tag getTag(String tagId) { + Tag tag = tagMapper.findById(tagId); + if (tag == null) { + throw new IllegalArgumentException("Tag not found: " + tagId); + } + return tag; + } + + /** + * 根据名称获取标签 + */ + @Transactional(readOnly = true) + public Tag getTagByName(String name) { + Tag tag = tagMapper.findByName(name); + if (tag == null) { + throw new IllegalArgumentException("Tag not found: " + name); + } + return tag; + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetStatusType.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetStatusType.java new file mode 100644 index 0000000..d9b2ff2 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetStatusType.java @@ -0,0 +1,41 @@ +package com.datamate.datamanagement.common.enums; + +/** + * 数据集状态类型 + *

数据集可以处于以下几种状态: + *

草稿(DRAFT):数据集正在创建中,尚未完成。 + *

活动(ACTIVE):数据集处于活动状态, 可以被查询和使用,也可以被更新和删除。 + *

处理中(PROCESSING):数据集正在处理中,可能需要一些时间,处理完成后会变成活动状态。 + *

已归档(ARCHIVED):数据集已被归档,不可以更新文件,可以解锁变成活动状态。 + *

已发布(PUBLISHED):数据集已被发布,可供外部使用,外部用户可以查询和使用数据集。 + *

已弃用(DEPRECATED):数据集已被弃用,不建议再使用。 + * + * @author dallas + * @since 2025-10-17 + */ +public enum DatasetStatusType { + /** + * 草稿状态 + */ + DRAFT, + /** + * 活动状态 + */ + ACTIVE, + /** + * 处理中状态 + */ + PROCESSING, + /** + * 已归档状态 + */ + ARCHIVED, + /** + * 已发布状态 + */ + PUBLISHED, + /** + * 已弃用状态 + */ + DEPRECATED +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetType.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetType.java new file mode 100644 index 0000000..70e895a --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/common/enums/DatasetType.java @@ -0,0 +1,28 @@ +package com.datamate.datamanagement.common.enums; + +import lombok.Getter; + +/** + * 数据集类型值对象 + * + * @author DataMate + * @since 2025-10-15 + */ +public enum DatasetType { + TEXT("text", "文本数据集"), + IMAGE("image", "图像数据集"), + AUDIO("audio", "音频数据集"), + VIDEO("video", "视频数据集"), + OTHER("other", "其他数据集"); + + @Getter + private final String code; + + @Getter + private final String description; + + DatasetType(String code, String description) { + this.code = code; + this.description = description; + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/contants/DatasetConstant.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/contants/DatasetConstant.java new file mode 100644 index 0000000..a6fe6e9 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/contants/DatasetConstant.java @@ -0,0 +1,11 @@ +package com.datamate.datamanagement.domain.contants; + +/** + * 数据集常量 + */ +public interface DatasetConstant { + /** + * 服务ID + */ + String SERVICE_ID = "DATA_MANAGEMENT"; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Dataset.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Dataset.java new file mode 100644 index 0000000..a3d55df --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Dataset.java @@ -0,0 +1,146 @@ +package com.datamate.datamanagement.domain.model.dataset; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.datamate.common.domain.model.base.BaseEntity; +import com.datamate.datamanagement.common.enums.DatasetStatusType; +import com.datamate.datamanagement.common.enums.DatasetType; +import lombok.Getter; +import lombok.Setter; + +import java.io.File; +import java.time.LocalDateTime; +import java.util.*; + +/** + * 数据集实体(与数据库表 t_dm_datasets 对齐) + */ +@Getter +@Setter +@TableName(value = "t_dm_datasets", autoResultMap = true) +public class Dataset extends BaseEntity { + /** + * 数据集名称 + */ + private String name; + /** + * 数据集描述 + */ + private String description; + /** + * 数据集类型 + */ + private DatasetType datasetType; + /** + * 数据集分类 + */ + private String category; + /** + * 数据集路径 + */ + private String path; + /** + * 数据集格式 + */ + private String format; + /** + * 数据集模式信息,JSON格式, 用于解析当前数据集的文件结构 + */ + private String schemaInfo; + /** + * 数据集大小(字节) + */ + private Long sizeBytes = 0L; + /** + * 文件数量 + */ + private Long fileCount = 0L; + /** + * 记录数量 + */ + private Long recordCount = 0L; + /** + * 数据集保留天数 + */ + private Integer retentionDays = 0; + /** + * 标签列表, JSON格式 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Collection tags = new HashSet<>(); + /** + * 额外元数据,JSON格式 + */ + private String metadata; + /** + * 数据集状态 + */ + private DatasetStatusType status; + /** + * 是否为公共数据集 + */ + private Boolean isPublic = false; + /** + * 是否为精选数据集 + */ + private Boolean isFeatured = false; + /** + * 数据集版本号 + */ + private Long version = 0L; + + @TableField(exist = false) + private List files = new ArrayList<>(); + + public Dataset() { + } + + public Dataset(String name, String description, DatasetType datasetType, String category, String path, + String format, DatasetStatusType status, String createdBy) { + this.name = name; + this.description = description; + this.datasetType = datasetType; + this.category = category; + this.path = path; + this.format = format; + this.status = status; + this.createdBy = createdBy; + this.createdAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + } + + public void initCreateParam(String datasetBasePath) { + this.id = UUID.randomUUID().toString(); + this.path = datasetBasePath + File.separator + this.id; + this.status = DatasetStatusType.DRAFT; + } + + public void updateBasicInfo(String name, String description, String category) { + if (name != null && !name.isEmpty()) this.name = name; + if (description != null) this.description = description; + if (category != null) this.category = category; + this.updatedAt = LocalDateTime.now(); + } + + public void updateStatus(DatasetStatusType status, String updatedBy) { + this.status = status; + this.updatedBy = updatedBy; + this.updatedAt = LocalDateTime.now(); + } + + public void addFile(DatasetFile file) { + this.files.add(file); + this.fileCount = this.fileCount + 1; + this.sizeBytes = this.sizeBytes + (file.getFileSize() != null ? file.getFileSize() : 0L); + this.updatedAt = LocalDateTime.now(); + } + + public void removeFile(DatasetFile file) { + if (this.files.remove(file)) { + this.fileCount = Math.max(0, this.fileCount - 1); + this.sizeBytes = Math.max(0, this.sizeBytes - (file.getFileSize() != null ? file.getFileSize() : 0L)); + this.updatedAt = LocalDateTime.now(); + } + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFile.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFile.java new file mode 100644 index 0000000..85fb60b --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFile.java @@ -0,0 +1,35 @@ +package com.datamate.datamanagement.domain.model.dataset; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 数据集文件实体(与数据库表 t_dm_dataset_files 对齐) + */ +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("t_dm_dataset_files") +public class DatasetFile { + @TableId + private String id; // UUID + private String datasetId; // UUID + private String fileName; + private String filePath; + private String fileType; // JPG/PNG/DCM/TXT + private Long fileSize; // bytes + private String checkSum; + private List tags; + private String metadata; + private String status; // UPLOADED, PROCESSING, COMPLETED, ERROR + private LocalDateTime uploadTime; + private LocalDateTime lastAccessTime; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFileUploadCheckInfo.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFileUploadCheckInfo.java new file mode 100644 index 0000000..3c1917d --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/DatasetFileUploadCheckInfo.java @@ -0,0 +1,18 @@ +package com.datamate.datamanagement.domain.model.dataset; + +import com.datamate.common.domain.model.UploadCheckInfo; +import lombok.Getter; +import lombok.Setter; + +/** + * 数据集文件上传检查信息 + */ +@Getter +@Setter +public class DatasetFileUploadCheckInfo extends UploadCheckInfo { + /** 数据集id */ + private String datasetId; + + /** 是否为压缩包上传 */ + private boolean hasArchive; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/StatusConstants.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/StatusConstants.java new file mode 100644 index 0000000..05d232d --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/StatusConstants.java @@ -0,0 +1,33 @@ +package com.datamate.datamanagement.domain.model.dataset; + +/** + * 状态常量类 - 统一管理所有状态枚举值 + */ +public final class StatusConstants { + + /** + * 数据集状态 + */ + public static final class DatasetStatuses { + public static final String DRAFT = "DRAFT"; + public static final String ACTIVE = "ACTIVE"; + public static final String ARCHIVED = "ARCHIVED"; + public static final String PROCESSING = "PROCESSING"; + + private DatasetStatuses() {} + } + + /** + * 数据集文件状态 + */ + public static final class DatasetFileStatuses { + public static final String UPLOADED = "UPLOADED"; + public static final String PROCESSING = "PROCESSING"; + public static final String COMPLETED = "COMPLETED"; + public static final String ERROR = "ERROR"; + + private DatasetFileStatuses() {} + } + + private StatusConstants() {} +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Tag.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Tag.java new file mode 100644 index 0000000..a37b5f7 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/domain/model/dataset/Tag.java @@ -0,0 +1,33 @@ +package com.datamate.datamanagement.domain.model.dataset; + +import com.datamate.common.domain.model.base.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 标签实体(与数据库表 t_dm_tags 对齐) + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Tag extends BaseEntity { + private String name; + private String description; + private String category; + private String color; + private Long usageCount = 0L; + + public Tag(String name, String description, String category, String color) { + this.name = name; + this.description = description; + this.category = category; + this.color = color; + } + + public void decrementUsage() { + if (this.usageCount != null && this.usageCount > 0) this.usageCount--; + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/CollectionTaskClient.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/CollectionTaskClient.java new file mode 100644 index 0000000..5deaa6b --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/CollectionTaskClient.java @@ -0,0 +1,22 @@ +package com.datamate.datamanagement.infrastructure.client; + +import com.datamate.common.infrastructure.common.Response; +import com.datamate.datamanagement.infrastructure.client.dto.CollectionTaskDetailResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +/** + * 数据归集服务 Feign Client + */ +@FeignClient(name = "collection-service", url = "${collection.service.url:http://localhost:8080}") +public interface CollectionTaskClient { + + /** + * 获取归集任务详情 + * @param taskId 任务ID + * @return 任务详情 + */ + @GetMapping("/api/data-collection/tasks/{id}") + Response getTaskDetail(@PathVariable("id") String taskId); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/CollectionTaskDetailResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/CollectionTaskDetailResponse.java new file mode 100644 index 0000000..5e38d8e --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/CollectionTaskDetailResponse.java @@ -0,0 +1,23 @@ +package com.datamate.datamanagement.infrastructure.client.dto; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 归集任务详情响应 + */ +@Data +public class CollectionTaskDetailResponse { + private String id; + private String name; + private String description; + private Map config; + private String status; + private String syncMode; + private String scheduleExpression; + private String lastExecutionId; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/LocalCollectionConfig.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/LocalCollectionConfig.java new file mode 100644 index 0000000..fff0491 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/client/dto/LocalCollectionConfig.java @@ -0,0 +1,21 @@ +package com.datamate.datamanagement.infrastructure.client.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 本地归集任务配置 + */ +@Data +public class LocalCollectionConfig { + /** + * 归集类型 + */ + private String type; + + /** + * 文件路径列表 + */ + private List filePaths; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementConfig.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementConfig.java new file mode 100644 index 0000000..623c334 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementConfig.java @@ -0,0 +1,37 @@ +package com.datamate.datamanagement.infrastructure.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.web.multipart.support.StandardServletMultipartResolver; + +/** + * 数据管理服务配置 + */ +@Configuration +@EnableTransactionManagement +@EnableCaching +@EnableConfigurationProperties(DataManagementProperties.class) +public class DataManagementConfig { + + /** + * 缓存管理器 + */ + @Bean + public CacheManager cacheManager() { + return new ConcurrentMapCacheManager("datasets", "datasetFiles", "tags"); + } + + /** + * 文件上传解析器 + */ + @Bean + public StandardServletMultipartResolver multipartResolver() { + StandardServletMultipartResolver resolver = new StandardServletMultipartResolver(); + return resolver; + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java new file mode 100644 index 0000000..6a91a1d --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java @@ -0,0 +1,82 @@ +package com.datamate.datamanagement.infrastructure.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 数据管理服务配置属性 + */ +@Configuration +@ConfigurationProperties(prefix = "datamanagement") +public class DataManagementProperties { + + private FileStorage fileStorage = new FileStorage(); + private Cache cache = new Cache(); + + public FileStorage getFileStorage() { + return fileStorage; + } + + public void setFileStorage(FileStorage fileStorage) { + this.fileStorage = fileStorage; + } + + public Cache getCache() { + return cache; + } + + public void setCache(Cache cache) { + this.cache = cache; + } + + public static class FileStorage { + private String uploadDir = "./uploads"; + private long maxFileSize = 10485760; // 10MB + private long maxRequestSize = 52428800; // 50MB + + public String getUploadDir() { + return uploadDir; + } + + public void setUploadDir(String uploadDir) { + this.uploadDir = uploadDir; + } + + public long getMaxFileSize() { + return maxFileSize; + } + + public void setMaxFileSize(long maxFileSize) { + this.maxFileSize = maxFileSize; + } + + public long getMaxRequestSize() { + return maxRequestSize; + } + + public void setMaxRequestSize(long maxRequestSize) { + this.maxRequestSize = maxRequestSize; + } + } + + public static class Cache { + private int ttl = 3600; // 1 hour + private int maxSize = 1000; + + public int getTtl() { + return ttl; + } + + public void setTtl(int ttl) { + this.ttl = ttl; + } + + public int getMaxSize() { + return maxSize; + } + + public void setMaxSize(int maxSize) { + this.maxSize = maxSize; + } + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/exception/DataManagementErrorCode.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/exception/DataManagementErrorCode.java new file mode 100644 index 0000000..3be421c --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/exception/DataManagementErrorCode.java @@ -0,0 +1,39 @@ +package com.datamate.datamanagement.infrastructure.exception; + +import com.datamate.common.infrastructure.exception.ErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据管理模块错误码 + * + * @author dallas + * @since 2025-10-20 + */ +@Getter +@AllArgsConstructor +public enum DataManagementErrorCode implements ErrorCode { + /** + * 数据集不存在 + */ + DATASET_NOT_FOUND("data_management.0001", "数据集不存在"), + /** + * 数据集已存在 + */ + DATASET_ALREADY_EXISTS("data_management.0002", "数据集已存在"), + /** + * 数据集状态错误 + */ + DATASET_STATUS_ERROR("data_management.0003", "数据集状态错误"), + /** + * 数据集标签不存在 + */ + DATASET_TAG_NOT_FOUND("data_management.0004", "数据集标签不存在"), + /** + * 数据集标签已存在 + */ + DATASET_TAG_ALREADY_EXISTS("data_management.0005", "数据集标签已存在"); + + private final String code; + private final String message; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetFileMapper.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetFileMapper.java new file mode 100644 index 0000000..6b0429c --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetFileMapper.java @@ -0,0 +1,30 @@ +package com.datamate.datamanagement.infrastructure.persistence.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.session.RowBounds; + +import java.util.List; + +@Mapper +public interface DatasetFileMapper extends BaseMapper { + DatasetFile findById(@Param("id") String id); + List findByDatasetId(@Param("datasetId") String datasetId, RowBounds rowBounds); + List findByDatasetIdAndStatus(@Param("datasetId") String datasetId, @Param("status") String status, RowBounds rowBounds); + List findByDatasetIdAndFileType(@Param("datasetId") String datasetId, @Param("fileType") String fileType, RowBounds rowBounds); + Long countByDatasetId(@Param("datasetId") String datasetId); + Long countCompletedByDatasetId(@Param("datasetId") String datasetId); + Long sumSizeByDatasetId(@Param("datasetId") String datasetId); + DatasetFile findByDatasetIdAndFileName(@Param("datasetId") String datasetId, @Param("fileName") String fileName); + List findAllByDatasetId(@Param("datasetId") String datasetId); + List findByCriteria(@Param("datasetId") String datasetId, + @Param("fileType") String fileType, + @Param("status") String status, + RowBounds rowBounds); + + int insert(DatasetFile file); + int update(DatasetFile file); + int deleteById(@Param("id") String id); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetMapper.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetMapper.java new file mode 100644 index 0000000..4450511 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/DatasetMapper.java @@ -0,0 +1,33 @@ +package com.datamate.datamanagement.infrastructure.persistence.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.datamate.datamanagement.domain.model.dataset.Dataset; +import com.datamate.datamanagement.interfaces.dto.AllDatasetStatisticsResponse; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.session.RowBounds; + +import java.util.List; + +@Mapper +public interface DatasetMapper extends BaseMapper { + Dataset findById(@Param("id") String id); + Dataset findByName(@Param("name") String name); + List findByStatus(@Param("status") String status); + List findByCreatedBy(@Param("createdBy") String createdBy, RowBounds rowBounds); + List findByTypeCode(@Param("typeCode") String typeCode, RowBounds rowBounds); + List findByTagNames(@Param("tagNames") List tagNames, RowBounds rowBounds); + List findByKeyword(@Param("keyword") String keyword, RowBounds rowBounds); + List findByCriteria(@Param("typeCode") String typeCode, + @Param("status") String status, + @Param("keyword") String keyword, + @Param("tagNames") List tagNames, + RowBounds rowBounds); + long countByCriteria(@Param("typeCode") String typeCode, + @Param("status") String status, + @Param("keyword") String keyword, + @Param("tagNames") List tagNames); + + int deleteById(@Param("id") String id); + AllDatasetStatisticsResponse getAllDatasetStatistics(); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/TagMapper.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/TagMapper.java new file mode 100644 index 0000000..84c1bb2 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/TagMapper.java @@ -0,0 +1,27 @@ +package com.datamate.datamanagement.infrastructure.persistence.mapper; + +import com.datamate.datamanagement.domain.model.dataset.Tag; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TagMapper { + Tag findById(@Param("id") String id); + Tag findByName(@Param("name") String name); + List findByNameIn(@Param("list") List names); + List findByIdIn(@Param("ids") List ids); + List findByKeyword(@Param("keyword") String keyword); + List findAllByOrderByUsageCountDesc(); + + int insert(Tag tag); + int update(Tag tag); + int updateUsageCount(@Param("id") String id, @Param("usageCount") Long usageCount); + + // Relations with dataset + int insertDatasetTag(@Param("datasetId") String datasetId, @Param("tagId") String tagId); + int deleteDatasetTagsByDatasetId(@Param("datasetId") String datasetId); + List findByDatasetId(@Param("datasetId") String datasetId); + void deleteTagsById(@Param("ids") List ids); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetFileRepository.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetFileRepository.java new file mode 100644 index 0000000..de9880f --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetFileRepository.java @@ -0,0 +1,27 @@ +package com.datamate.datamanagement.infrastructure.persistence.repository; + +import com.baomidou.mybatisplus.extension.repository.IRepository; +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import org.apache.ibatis.session.RowBounds; + +import java.util.List; + +/** + * 数据集文件仓储接口 + * + * @author dallas + * @since 2025-10-15 + */ +public interface DatasetFileRepository extends IRepository { + Long countByDatasetId(String datasetId); + + Long countCompletedByDatasetId(String datasetId); + + Long sumSizeByDatasetId(String datasetId); + + List findAllByDatasetId(String datasetId); + + DatasetFile findByDatasetIdAndFileName(String datasetId, String fileName); + + List findByCriteria(String datasetId, String fileType, String status, RowBounds bounds); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetRepository.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetRepository.java new file mode 100644 index 0000000..b257161 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/DatasetRepository.java @@ -0,0 +1,29 @@ +package com.datamate.datamanagement.infrastructure.persistence.repository; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.repository.IRepository; +import com.datamate.datamanagement.domain.model.dataset.Dataset; +import com.datamate.datamanagement.interfaces.dto.AllDatasetStatisticsResponse; +import com.datamate.datamanagement.interfaces.dto.DatasetPagingQuery; +import org.apache.ibatis.session.RowBounds; + +import java.util.List; + + +/** + * 数据集仓储层 + * + * @author dallas + * @since 2025-10-15 + */ +public interface DatasetRepository extends IRepository { + Dataset findByName(String name); + + List findByCriteria(String type, String status, String keyword, List tagList, RowBounds bounds); + + long countByCriteria(String type, String status, String keyword, List tagList); + + AllDatasetStatisticsResponse getAllDatasetStatistics(); + + IPage findByCriteria(IPage page, DatasetPagingQuery query); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetFileRepositoryImpl.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetFileRepositoryImpl.java new file mode 100644 index 0000000..277e576 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetFileRepositoryImpl.java @@ -0,0 +1,54 @@ +package com.datamate.datamanagement.infrastructure.persistence.repository.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.repository.CrudRepository; +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import com.datamate.datamanagement.infrastructure.persistence.mapper.DatasetFileMapper; +import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetFileRepository; +import lombok.RequiredArgsConstructor; +import org.apache.ibatis.session.RowBounds; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 数据集文件仓储实现类 + * + * @author dallas + * @since 2025-10-15 + */ +@Repository +@RequiredArgsConstructor +public class DatasetFileRepositoryImpl extends CrudRepository implements DatasetFileRepository { + private final DatasetFileMapper datasetFileMapper; + + @Override + public Long countByDatasetId(String datasetId) { + return datasetFileMapper.selectCount(new LambdaQueryWrapper().eq(DatasetFile::getDatasetId, datasetId)); + } + + @Override + public Long countCompletedByDatasetId(String datasetId) { + return datasetFileMapper.countCompletedByDatasetId(datasetId); + } + + @Override + public Long sumSizeByDatasetId(String datasetId) { + return datasetFileMapper.sumSizeByDatasetId(datasetId); + } + + @Override + public List findAllByDatasetId(String datasetId) { + return datasetFileMapper.findAllByDatasetId(datasetId); + } + + @Override + public DatasetFile findByDatasetIdAndFileName(String datasetId, String fileName) { + return datasetFileMapper.findByDatasetIdAndFileName(datasetId, fileName); + } + + @Override + public List findByCriteria(String datasetId, String fileType, String status, RowBounds bounds) { + return datasetFileMapper.findByCriteria(datasetId, fileType, status, bounds); + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetRepositoryImpl.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetRepositoryImpl.java new file mode 100644 index 0000000..3fc5458 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/DatasetRepositoryImpl.java @@ -0,0 +1,73 @@ +package com.datamate.datamanagement.infrastructure.persistence.repository.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.repository.CrudRepository; +import com.datamate.datamanagement.domain.model.dataset.Dataset; +import com.datamate.datamanagement.infrastructure.persistence.mapper.DatasetMapper; +import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetRepository; +import com.datamate.datamanagement.interfaces.dto.AllDatasetStatisticsResponse; +import com.datamate.datamanagement.interfaces.dto.DatasetPagingQuery; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.RowBounds; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 数据集仓储层实现类 + * + * @author dallas + * @since 2025-10-15 + */ +@Repository +@RequiredArgsConstructor +public class DatasetRepositoryImpl extends CrudRepository implements DatasetRepository { + private final DatasetMapper datasetMapper; + + @Override + public Dataset findByName(String name) { + return datasetMapper.selectOne(new LambdaQueryWrapper().eq(Dataset::getName, name)); + } + + @Override + public List findByCriteria(String type, String status, String keyword, List tagList, + RowBounds bounds) { + return datasetMapper.findByCriteria(type, status, keyword, tagList, bounds); + } + + @Override + public long countByCriteria(String type, String status, String keyword, List tagList) { + return datasetMapper.countByCriteria(type, status, keyword, tagList); + } + + @Override + public AllDatasetStatisticsResponse getAllDatasetStatistics() { + return datasetMapper.getAllDatasetStatistics(); + } + + + @Override + public IPage findByCriteria(IPage page, DatasetPagingQuery query) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(query.getType() != null, Dataset::getDatasetType, query.getType()) + .eq(query.getStatus() != null, Dataset::getStatus, query.getStatus()) + .like(StringUtils.isNotBlank(query.getKeyword()), Dataset::getName, query.getKeyword()) + .like(StringUtils.isNotBlank(query.getKeyword()), Dataset::getDescription, query.getKeyword()); + + /* + 标签过滤 {@link Tag} + */ + for (String tagName : query.getTags()) { + wrapper.and(w -> + w.apply("tags IS NOT NULL " + + "AND JSON_VALID(tags) = 1 " + + "AND JSON_LENGTH(tags) > 0 " + + "AND JSON_SEARCH(tags, 'one', {0}, NULL, '$[*].name') IS NOT NULL", tagName) + ); + } + wrapper.orderByDesc(Dataset::getCreatedAt); + return datasetMapper.selectPage(page, wrapper); + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/DatasetConverter.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/DatasetConverter.java new file mode 100644 index 0000000..0247ffe --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/DatasetConverter.java @@ -0,0 +1,53 @@ +package com.datamate.datamanagement.interfaces.converter; + +import com.datamate.datamanagement.interfaces.dto.CreateDatasetRequest; +import com.datamate.datamanagement.interfaces.dto.DatasetFileResponse; +import com.datamate.datamanagement.interfaces.dto.DatasetResponse; +import com.datamate.datamanagement.interfaces.dto.UploadFileRequest; +import com.datamate.common.domain.model.ChunkUploadRequest; +import com.datamate.datamanagement.domain.model.dataset.Dataset; +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import com.datamate.datamanagement.interfaces.dto.*; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 数据集文件转换器 + */ +@Mapper +public interface DatasetConverter { + /** 单例实例 */ + DatasetConverter INSTANCE = Mappers.getMapper(DatasetConverter.class); + + /** + * 将数据集转换为响应 + */ + @Mapping(source = "sizeBytes", target = "totalSize") + @Mapping(source = "path", target = "targetLocation") + DatasetResponse convertToResponse(Dataset dataset); + + /** + * 将数据集转换为响应 + */ + @Mapping(target = "tags", ignore = true) + Dataset convertToDataset(CreateDatasetRequest createDatasetRequest); + + /** + * 将上传文件请求转换为分片上传请求 + */ + ChunkUploadRequest toChunkUploadRequest(UploadFileRequest uploadFileRequest); + + /** + * 将数据集转换为响应 + */ + List convertToResponse(List datasets); + + /** + * + * 将数据集文件转换为响应 + */ + DatasetFileResponse convertToResponse(DatasetFile datasetFile); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/TagConverter.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/TagConverter.java new file mode 100644 index 0000000..b5a007c --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/converter/TagConverter.java @@ -0,0 +1,30 @@ +package com.datamate.datamanagement.interfaces.converter; + +import com.datamate.datamanagement.domain.model.dataset.Tag; +import com.datamate.datamanagement.interfaces.dto.TagResponse; +import com.datamate.datamanagement.interfaces.dto.UpdateTagRequest; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 标签转换器 + */ +@Mapper +public interface TagConverter { + /** 单例实例 */ + TagConverter INSTANCE = Mappers.getMapper(TagConverter.class); + + /** + * 将 UpdateTagRequest 转换为 Tag 实体 + * @param request 更新标签请求DTO + * @return 标签实体 + */ + Tag updateRequestToTag(UpdateTagRequest request); + + /** + * 将 Tag 实体转换为 TagResponse DTO + * @param tag 标签实体 + * @return 标签响应DTO + */ + TagResponse convertToResponse(Tag tag); +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/AllDatasetStatisticsResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/AllDatasetStatisticsResponse.java new file mode 100644 index 0000000..7da863f --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/AllDatasetStatisticsResponse.java @@ -0,0 +1,20 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +/** + * 所有数据集统计信息响应DTO + */ +@Getter +@Setter +public class AllDatasetStatisticsResponse { + /** 总数据集数 */ + private Integer totalDatasets; + + /** 总文件数 */ + private Long totalSize; + + /** 总大小(字节) */ + private Long totalFiles; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateDatasetRequest.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateDatasetRequest.java new file mode 100644 index 0000000..7e55657 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateDatasetRequest.java @@ -0,0 +1,35 @@ +package com.datamate.datamanagement.interfaces.dto; + +import com.datamate.datamanagement.common.enums.DatasetType; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * 创建数据集请求DTO + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class CreateDatasetRequest { + /** 数据集名称 */ + @NotBlank(message = "数据集名称不能为空") + private String name; + /** 数据集描述 */ + private String description; + /** 数据集类型 */ + @NotNull(message = "数据集类型不能为空") + private DatasetType datasetType; + /** 标签列表 */ + private List tags; + /** 数据源 */ + private String dataSource; + /** 目标位置 */ + private String targetLocation; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateTagRequest.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateTagRequest.java new file mode 100644 index 0000000..dca22bb --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/CreateTagRequest.java @@ -0,0 +1,18 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +/** + * 创建标签请求DTO + */ +@Getter +@Setter +public class CreateTagRequest { + /** 标签名称 */ + private String name; + /** 标签颜色 */ + private String color; + /** 标签描述 */ + private String description; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetFileResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetFileResponse.java new file mode 100644 index 0000000..ec06ff4 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetFileResponse.java @@ -0,0 +1,36 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +/** + * 数据集文件响应DTO + */ +@Getter +@Setter +public class DatasetFileResponse { + /** 文件ID */ + private String id; + /** 文件名 */ + private String fileName; + /** 原始文件名 */ + private String originalName; + /** 文件类型 */ + private String fileType; + /** 文件大小(字节) */ + private Long fileSize; + /** 文件状态 */ + private String status; + /** 文件描述 */ + private String description; + /** 文件路径 */ + private String filePath; + /** 上传时间 */ + private LocalDateTime uploadTime; + /** 最后更新时间 */ + private LocalDateTime lastAccessTime; + /** 上传者 */ + private String uploadedBy; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetPagingQuery.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetPagingQuery.java new file mode 100644 index 0000000..6016f4d --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetPagingQuery.java @@ -0,0 +1,42 @@ +package com.datamate.datamanagement.interfaces.dto; + +import com.datamate.common.interfaces.PagingQuery; +import com.datamate.datamanagement.common.enums.DatasetStatusType; +import com.datamate.datamanagement.common.enums.DatasetType; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * 数据集分页查询请求 + * + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class DatasetPagingQuery extends PagingQuery { + /** + * 数据集类型过滤 + */ + private DatasetType type; + + /** + * 标签名过滤 + */ + private List tags = new ArrayList<>(); + + /** + * 关键词搜索(名称或描述) + */ + private String keyword; + + /** + * 状态过滤 + */ + private DatasetStatusType status; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetResponse.java new file mode 100644 index 0000000..e7b1779 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetResponse.java @@ -0,0 +1,47 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 数据集响应DTO + */ +@Getter +@Setter +public class DatasetResponse { + /** 数据集ID */ + private String id; + /** 数据集名称 */ + private String name; + /** 数据集描述 */ + private String description; + /** 数据集类型 */ + private String datasetType; + /** 数据集状态 */ + private String status; + /** 标签列表 */ + private List tags; + /** 数据源 */ + private String dataSource; + /** 目标位置 */ + private String targetLocation; + /** 文件数量 */ + private Integer fileCount; + /** 总大小(字节) */ + private Long totalSize; + /** 完成率(0-100) */ + private Float completionRate; + /** 创建时间 */ + private LocalDateTime createdAt; + /** 更新时间 */ + private LocalDateTime updatedAt; + /** 创建者 */ + private String createdBy; + /** + * 更新者 + */ + private String updatedBy; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetStatisticsResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetStatisticsResponse.java new file mode 100644 index 0000000..6159f80 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetStatisticsResponse.java @@ -0,0 +1,26 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +/** + * 数据集统计信息响应DTO + */ +@Getter +@Setter +public class DatasetStatisticsResponse { + /** 总文件数 */ + private Integer totalFiles; + /** 已完成文件数 */ + private Integer completedFiles; + /** 总大小(字节) */ + private Long totalSize; + /** 完成率(0-100) */ + private Float completionRate; + /** 文件类型分布 */ + private Map fileTypeDistribution; + /** 状态分布 */ + private Map statusDistribution; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetTypeResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetTypeResponse.java new file mode 100644 index 0000000..6f53f7e --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/DatasetTypeResponse.java @@ -0,0 +1,24 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 数据集类型响应DTO + */ +@Getter +@Setter +public class DatasetTypeResponse { + /** 类型编码 */ + private String code; + /** 类型名称 */ + private String name; + /** 类型描述 */ + private String description; + /** 支持的文件格式 */ + private List supportedFormats; + /** 图标 */ + private String icon; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetFileResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetFileResponse.java new file mode 100644 index 0000000..9e8100b --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetFileResponse.java @@ -0,0 +1,28 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 数据集文件分页响应DTO + */ +@Getter +@Setter +public class PagedDatasetFileResponse { + /** 文件内容列表 */ + private List content; + /** 当前页码 */ + private Integer page; + /** 每页大小 */ + private Integer size; + /** 总元素数 */ + private Integer totalElements; + /** 总页数 */ + private Integer totalPages; + /** 是否为第一页 */ + private Boolean first; + /** 是否为最后一页 */ + private Boolean last; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetResponse.java new file mode 100644 index 0000000..12d6c64 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/PagedDatasetResponse.java @@ -0,0 +1,28 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 数据集分页响应DTO + */ +@Getter +@Setter +public class PagedDatasetResponse { + /** 数据集内容列表 */ + private List content; + /** 当前页码 */ + private Integer page; + /** 每页大小 */ + private Integer size; + /** 总元素数 */ + private Integer totalElements; + /** 总页数 */ + private Integer totalPages; + /** 是否为第一页 */ + private Boolean first; + /** 是否为最后一页 */ + private Boolean last; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/TagResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/TagResponse.java new file mode 100644 index 0000000..e8294c3 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/TagResponse.java @@ -0,0 +1,22 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +/** + * 标签响应DTO + */ +@Getter +@Setter +public class TagResponse { + /** 标签ID */ + private String id; + /** 标签名称 */ + private String name; + /** 标签颜色 */ + private String color; + /** 标签描述 */ + private String description; + /** 使用次数 */ + private Integer usageCount; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateDatasetRequest.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateDatasetRequest.java new file mode 100644 index 0000000..3aea04f --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateDatasetRequest.java @@ -0,0 +1,25 @@ +package com.datamate.datamanagement.interfaces.dto; + +import com.datamate.datamanagement.common.enums.DatasetStatusType; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 更新数据集请求DTO + */ +@Getter +@Setter +public class UpdateDatasetRequest { + /** 数据集名称 */ + private String name; + /** 数据集描述 */ + private String description; + /** 归集任务id */ + private String dataSource; + /** 标签列表 */ + private List tags; + /** 数据集状态 */ + private DatasetStatusType status; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateTagRequest.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateTagRequest.java new file mode 100644 index 0000000..1fb6d13 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UpdateTagRequest.java @@ -0,0 +1,20 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +/** + * 更新标签请求DTO + */ +@Getter +@Setter +public class UpdateTagRequest { + /** 标签 ID */ + private String id; + /** 标签名称 */ + private String name; + /** 标签颜色 */ + private String color; + /** 标签描述 */ + private String description; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFileRequest.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFileRequest.java new file mode 100644 index 0000000..e8c2b69 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFileRequest.java @@ -0,0 +1,34 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.web.multipart.MultipartFile; + +/** + * 上传文件请求 + * 用于分块上传文件时的请求参数封装,支持大文件分片上传功能 + */ +@Getter +@Setter +public class UploadFileRequest { + /** 预上传返回的id,用来确认同一个任务 */ + private String reqId; + + /** 文件编号,用于标识批量上传中的第几个文件 */ + private int fileNo; + + /** 文件名称 */ + private String fileName; + + /** 文件总分块数量 */ + private int totalChunkNum; + + /** 当前分块编号,从1开始 */ + private int chunkNo; + + /** 上传的文件分块内容 */ + private MultipartFile file; + + /** 文件分块的校验和(十六进制字符串),用于验证文件完整性 */ + private String checkSumHex; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFilesPreRequest.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFilesPreRequest.java new file mode 100644 index 0000000..1bfcc12 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/UploadFilesPreRequest.java @@ -0,0 +1,22 @@ +package com.datamate.datamanagement.interfaces.dto; + +import jakarta.validation.constraints.Min; +import lombok.Getter; +import lombok.Setter; + +/** + * 切片上传预上传请求 + */ +@Getter +@Setter +public class UploadFilesPreRequest { + /** 是否为压缩包上传 */ + private boolean hasArchive; + + /** 总文件数量 */ + @Min(1) + private int totalFileNum; + + /** 总文件大小 */ + private long totalSize; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetController.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetController.java new file mode 100644 index 0000000..173a4bb --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetController.java @@ -0,0 +1,115 @@ +package com.datamate.datamanagement.interfaces.rest; + +import com.datamate.datamanagement.interfaces.dto.*; +import com.datamate.common.infrastructure.common.Response; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.datamate.common.interfaces.PagedResponse; +import com.datamate.datamanagement.application.DatasetApplicationService; +import com.datamate.datamanagement.domain.model.dataset.Dataset; +import com.datamate.datamanagement.interfaces.converter.DatasetConverter; +import com.datamate.datamanagement.interfaces.dto.*; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 数据集 REST 控制器 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/data-management/datasets") +public class DatasetController { + private final DatasetApplicationService datasetApplicationService; + + /** + * 获取数据集列表 + * + * @param query 分页查询参数 + * @return 分页的数据集列表 + */ + @GetMapping + public PagedResponse getDatasets(DatasetPagingQuery query) { + return datasetApplicationService.getDatasets(query); + } + + /** + * 创建数据集 + * + * @param createDatasetRequest 创建数据集请求参数 + * @return 创建的数据集响应 + */ + @PostMapping + public DatasetResponse createDataset(@RequestBody @Valid CreateDatasetRequest createDatasetRequest) { + Dataset dataset = datasetApplicationService.createDataset(createDatasetRequest); + return DatasetConverter.INSTANCE.convertToResponse(dataset); + } + + /** + * 根据ID获取数据集详情 + * + * @param datasetId 数据集ID + * @return 数据集响应 + */ + @GetMapping("/{datasetId}") + public DatasetResponse getDatasetById(@PathVariable("datasetId") String datasetId) { + Dataset dataset = datasetApplicationService.getDataset(datasetId); + return DatasetConverter.INSTANCE.convertToResponse(dataset); + } + + /** + * 根据ID更新数据集 + * + * @param datasetId 数据集ID + * @param updateDatasetRequest 更新数据集请求参数 + * @return 更新后的数据集响应 + */ + @PutMapping("/{datasetId}") + public DatasetResponse updateDataset(@PathVariable("datasetId") String datasetId, + @RequestBody UpdateDatasetRequest updateDatasetRequest) { + Dataset dataset = datasetApplicationService.updateDataset(datasetId, updateDatasetRequest); + return DatasetConverter.INSTANCE.convertToResponse(dataset); + } + + /** + * 根据ID删除数据集 + * + * @param datasetId 数据集ID + */ + @DeleteMapping("/{datasetId}") + public void deleteDataset(@PathVariable("datasetId") String datasetId) { + datasetApplicationService.deleteDataset(datasetId); + } + + @GetMapping("/{datasetId}/statistics") + public ResponseEntity> getDatasetStatistics( + @PathVariable("datasetId") String datasetId) { + try { + Map stats = datasetApplicationService.getDatasetStatistics(datasetId); + + DatasetStatisticsResponse response = new DatasetStatisticsResponse(); + response.setTotalFiles((Integer) stats.get("totalFiles")); + response.setCompletedFiles((Integer) stats.get("completedFiles")); + response.setTotalSize((Long) stats.get("totalSize")); + response.setCompletionRate((Float) stats.get("completionRate")); + response.setFileTypeDistribution((Map) stats.get("fileTypeDistribution")); + response.setStatusDistribution((Map) stats.get("statusDistribution")); + + return ResponseEntity.ok(Response.ok(response)); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } + } + + @GetMapping("/statistics") + public ResponseEntity> getAllStatistics() { + return ResponseEntity.ok(Response.ok(datasetApplicationService.getAllDatasetStatistics())); + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetFileController.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetFileController.java new file mode 100644 index 0000000..24fe4c3 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetFileController.java @@ -0,0 +1,163 @@ +package com.datamate.datamanagement.interfaces.rest; + +import com.datamate.common.infrastructure.common.IgnoreResponseWrap; +import com.datamate.common.infrastructure.common.Response; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.datamate.datamanagement.application.DatasetFileApplicationService; +import com.datamate.datamanagement.domain.model.dataset.DatasetFile; +import com.datamate.datamanagement.interfaces.converter.DatasetConverter; +import com.datamate.datamanagement.interfaces.dto.DatasetFileResponse; +import com.datamate.datamanagement.interfaces.dto.PagedDatasetFileResponse; +import com.datamate.datamanagement.interfaces.dto.UploadFileRequest; +import com.datamate.datamanagement.interfaces.dto.UploadFilesPreRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.stream.Collectors; + +/** + * 数据集文件 REST 控制器(UUID 模式) + */ +@Slf4j +@RestController +@RequestMapping("/data-management/datasets/{datasetId}/files") +public class DatasetFileController { + + private final DatasetFileApplicationService datasetFileApplicationService; + + @Autowired + public DatasetFileController(DatasetFileApplicationService datasetFileApplicationService) { + this.datasetFileApplicationService = datasetFileApplicationService; + } + + @GetMapping + public ResponseEntity> getDatasetFiles( + @PathVariable("datasetId") String datasetId, + @RequestParam(value = "page", required = false, defaultValue = "0") Integer page, + @RequestParam(value = "size", required = false, defaultValue = "20") Integer size, + @RequestParam(value = "fileType", required = false) String fileType, + @RequestParam(value = "status", required = false) String status) { + Pageable pageable = PageRequest.of(page != null ? page : 0, size != null ? size : 20); + + Page filesPage = datasetFileApplicationService.getDatasetFiles( + datasetId, fileType, status, pageable); + + PagedDatasetFileResponse response = new PagedDatasetFileResponse(); + response.setContent(filesPage.getContent().stream() + .map(DatasetConverter.INSTANCE::convertToResponse) + .collect(Collectors.toList())); + response.setPage(filesPage.getNumber()); + response.setSize(filesPage.getSize()); + response.setTotalElements((int) filesPage.getTotalElements()); + response.setTotalPages(filesPage.getTotalPages()); + response.setFirst(filesPage.isFirst()); + response.setLast(filesPage.isLast()); + + return ResponseEntity.ok(Response.ok(response)); + } + + @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity> uploadDatasetFile( + @PathVariable("datasetId") String datasetId, + @RequestPart(value = "file", required = false) MultipartFile file) { + try { + DatasetFile datasetFile = datasetFileApplicationService.uploadFile(datasetId, file); + + return ResponseEntity.status(HttpStatus.CREATED).body(Response.ok(DatasetConverter.INSTANCE.convertToResponse(datasetFile))); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } catch (Exception e) { + log.error("upload fail", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } + } + + @GetMapping("/{fileId}") + public ResponseEntity> getDatasetFileById( + @PathVariable("datasetId") String datasetId, + @PathVariable("fileId") String fileId) { + try { + DatasetFile datasetFile = datasetFileApplicationService.getDatasetFile(datasetId, fileId); + return ResponseEntity.ok(Response.ok(DatasetConverter.INSTANCE.convertToResponse(datasetFile))); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } + } + + @DeleteMapping("/{fileId}") + public ResponseEntity> deleteDatasetFile( + @PathVariable("datasetId") String datasetId, + @PathVariable("fileId") String fileId) { + try { + datasetFileApplicationService.deleteDatasetFile(datasetId, fileId); + return ResponseEntity.ok().build(); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } + } + + @IgnoreResponseWrap + @GetMapping(value = "/{fileId}/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public ResponseEntity downloadDatasetFileById( + @PathVariable("datasetId") String datasetId, + @PathVariable("fileId") String fileId) { + try { + DatasetFile datasetFile = datasetFileApplicationService.getDatasetFile(datasetId, fileId); + Resource resource = datasetFileApplicationService.downloadFile(datasetId, fileId); + + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + datasetFile.getFileName() + "\"") + .body(resource); + } catch (IllegalArgumentException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } + + @IgnoreResponseWrap + @GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public void downloadDatasetFileAsZip(@PathVariable("datasetId") String datasetId, HttpServletResponse response) { + datasetFileApplicationService.downloadDatasetFileAsZip(datasetId, response); + } + + /** + * 文件上传请求 + * + * @param request 批量文件上传请求 + * @return 批量上传请求id + */ + @PostMapping("/upload/pre-upload") + public ResponseEntity> preUpload(@PathVariable("datasetId") String datasetId, @RequestBody @Valid UploadFilesPreRequest request) { + + return ResponseEntity.ok(Response.ok(datasetFileApplicationService.preUpload(request, datasetId))); + } + + /** + * 分块上传 + * + * @param uploadFileRequest 上传文件请求 + */ + @PostMapping("/upload/chunk") + public ResponseEntity chunkUpload(@PathVariable("datasetId") String datasetId, UploadFileRequest uploadFileRequest) { + log.info("file upload reqId:{}, fileNo:{}, total chunk num:{}, current chunkNo:{}", + uploadFileRequest.getReqId(), uploadFileRequest.getFileNo(), uploadFileRequest.getTotalChunkNum(), + uploadFileRequest.getChunkNo()); + datasetFileApplicationService.chunkUpload(datasetId, uploadFileRequest); + return ResponseEntity.ok().build(); + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetTypeController.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetTypeController.java new file mode 100644 index 0000000..dfc3600 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/DatasetTypeController.java @@ -0,0 +1,53 @@ +package com.datamate.datamanagement.interfaces.rest; + +import com.datamate.datamanagement.interfaces.dto.DatasetTypeResponse; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; + +/** + * 数据集类型 REST 控制器 + */ +@RestController +@RequestMapping("/data-management/dataset-types") +public class DatasetTypeController { + + /** + * 获取所有支持的数据集类型 + * @return 数据集类型列表 + */ + @GetMapping + public List getDatasetTypes() { + return Arrays.asList( + createDatasetType("IMAGE", "图像数据集", "用于机器学习的图像数据集", Arrays.asList("jpg", "jpeg", "png", "bmp", "gif")), + createDatasetType("TEXT", "文本数据集", "用于文本分析的文本数据集", Arrays.asList("txt", "csv", "json", "xml")), + createDatasetType("AUDIO", "音频数据集", "用于音频处理的音频数据集", Arrays.asList("wav", "mp3", "flac", "aac")), + createDatasetType("VIDEO", "视频数据集", "用于视频分析的视频数据集", Arrays.asList("mp4", "avi", "mov", "mkv")), + createDatasetType("MULTIMODAL", "多模态数据集", "包含多种数据类型的数据集", List.of("*")) + ); + } + + private DatasetTypeResponse createDatasetType(String code, String name, String description, List supportedFormats) { + DatasetTypeResponse response = new DatasetTypeResponse(); + response.setCode(code); + response.setName(name); + response.setDescription(description); + response.setSupportedFormats(supportedFormats); + response.setIcon(getIconForType(code)); + return response; + } + + private String getIconForType(String typeCode) { + return switch (typeCode) { + case "IMAGE" -> "🖼️"; + case "TEXT" -> "📄"; + case "AUDIO" -> "🎵"; + case "VIDEO" -> "🎬"; + case "MULTIMODAL" -> "📊"; + default -> "📁"; + }; + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/TagController.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/TagController.java new file mode 100644 index 0000000..476caeb --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/TagController.java @@ -0,0 +1,85 @@ +package com.datamate.datamanagement.interfaces.rest; + +import com.datamate.common.infrastructure.common.Response; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import com.datamate.datamanagement.application.TagApplicationService; +import com.datamate.datamanagement.domain.model.dataset.Tag; +import com.datamate.datamanagement.interfaces.converter.TagConverter; +import com.datamate.datamanagement.interfaces.dto.CreateTagRequest; +import com.datamate.datamanagement.interfaces.dto.TagResponse; +import com.datamate.datamanagement.interfaces.dto.UpdateTagRequest; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 标签 REST 控制器(UUID 模式) + */ +@RestController +@RequestMapping("/data-management/tags") +public class TagController { + + private final TagApplicationService tagApplicationService; + + @Autowired + public TagController(TagApplicationService tagApplicationService) { + this.tagApplicationService = tagApplicationService; + } + + /** + * 查询标签列表 + */ + @GetMapping + public ResponseEntity>> getTags(@RequestParam(name = "keyword", required = false) String keyword) { + List tags = tagApplicationService.searchTags(keyword); + List response = tags.stream() + .map(TagConverter.INSTANCE::convertToResponse) + .collect(Collectors.toList()); + return ResponseEntity.ok(Response.ok(response)); + } + + /** + * 创建标签 + */ + @PostMapping + public ResponseEntity> createTag(@RequestBody CreateTagRequest createTagRequest) { + try { + Tag tag = tagApplicationService.createTag( + createTagRequest.getName(), + createTagRequest.getColor(), + createTagRequest.getDescription() + ); + return ResponseEntity.ok(Response.ok(TagConverter.INSTANCE.convertToResponse(tag))); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } + } + + /** + * 更新标签 + * + * @param updateTagRequest 更新参数 + * @return 更新结果 + */ + @PutMapping + public ResponseEntity> updateTag(@RequestBody @Valid UpdateTagRequest updateTagRequest) { + Tag tag = tagApplicationService.updateTag(TagConverter.INSTANCE.updateRequestToTag(updateTagRequest)); + return ResponseEntity.ok(Response.ok(TagConverter.INSTANCE.convertToResponse(tag))); + } + + @DeleteMapping + public ResponseEntity> deleteTag(@RequestParam(value = "ids") @Valid @Size(max = 10) List ids) { + try { + tagApplicationService.deleteTag(ids.stream().filter(StringUtils::isNoneBlank).distinct().toList()); + return ResponseEntity.ok(Response.ok(null)); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(Response.error(SystemErrorCode.UNKNOWN_ERROR, null)); + } + } +} diff --git a/backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml b/backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml new file mode 100644 index 0000000..df603c3 --- /dev/null +++ b/backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml @@ -0,0 +1,11 @@ +dataMate: + datamanagement: + file-storage: + upload-dir: ${FILE_UPLOAD_DIR:./uploads} + max-file-size: 10485760 # 10MB + max-request-size: 52428800 # 50MB + cache: + ttl: 3600 + max-size: 1000 +# MyBatis is configured centrally in main-application (mapper-locations & aliases) +# to avoid list overriding issues when importing multiple module configs. diff --git a/backend/services/data-management-service/src/main/resources/mappers/DatasetFileMapper.xml b/backend/services/data-management-service/src/main/resources/mappers/DatasetFileMapper.xml new file mode 100644 index 0000000..f5c6a1e --- /dev/null +++ b/backend/services/data-management-service/src/main/resources/mappers/DatasetFileMapper.xml @@ -0,0 +1,98 @@ + + + + + id, dataset_id, file_name, file_path, file_type, file_size, check_sum, tags, metadata, status, + upload_time, last_access_time, created_at, updated_at + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE t_dm_dataset_files + SET file_name = #{fileName}, + file_path = #{filePath}, + file_type = #{fileType}, + file_size = #{fileSize}, + upload_time = #{uploadTime}, + last_access_time = #{lastAccessTime}, + status = #{status} + WHERE id = #{id} + + + + DELETE FROM t_dm_dataset_files WHERE id = #{id} + + diff --git a/backend/services/data-management-service/src/main/resources/mappers/DatasetMapper.xml b/backend/services/data-management-service/src/main/resources/mappers/DatasetMapper.xml new file mode 100644 index 0000000..f266894 --- /dev/null +++ b/backend/services/data-management-service/src/main/resources/mappers/DatasetMapper.xml @@ -0,0 +1,152 @@ + + + + + + id, name, description, dataset_type, category, path, format, schema_info, size_bytes, file_count, record_count, + retention_days, tags, metadata, status, is_public, is_featured, version, created_at, updated_at, created_by, updated_by + + + + d.id AS id, + d.name AS name, + d.description AS description, + d.dataset_type AS dataset_type, + d.category AS category, + d.path AS path, + d.format AS format, + d.schema_info AS schema_info, + d.size_bytes AS size_bytes, + d.file_count AS file_count, + d.record_count AS record_count, + d.retention_days AS retention_days, + d.tags AS tags, + d.metadata AS metadata, + d.status AS status, + d.is_public AS is_public, + d.is_featured AS is_featured, + d.version AS version, + d.created_at AS created_at, + d.updated_at AS updated_at, + d.created_by AS created_by, + d.updated_by AS updated_by + + + + + + + + + + + + + + + + + + + + + + DELETE FROM t_dm_datasets WHERE id = #{id} + + + + diff --git a/backend/services/data-management-service/src/main/resources/mappers/TagMapper.xml b/backend/services/data-management-service/src/main/resources/mappers/TagMapper.xml new file mode 100644 index 0000000..accaad8 --- /dev/null +++ b/backend/services/data-management-service/src/main/resources/mappers/TagMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + id, name, description, category, color, usage_count, created_at, updated_at + + + + + + + + + + + + + + INSERT INTO t_dm_tags (id, name, description, category, color, usage_count) + VALUES (#{id}, #{name}, #{description}, #{category}, #{color}, #{usageCount}) + + + + UPDATE t_dm_tags + SET name = #{name}, + description = #{description}, + category = #{category}, + color = #{color}, + usage_count = #{usageCount} + WHERE id = #{id} + + + + UPDATE t_dm_tags + SET usage_count = #{usageCount} + WHERE id = #{id} + + + + + INSERT INTO t_dm_dataset_tags (dataset_id, tag_id) + VALUES (#{datasetId}, #{tagId}) + + + + DELETE FROM t_dm_dataset_tags WHERE dataset_id = #{datasetId} + + + + + + DELETE FROM t_dm_tags WHERE + id IN + + #{id} + + + + + diff --git a/backend/services/data-synthesis-service/pom.xml b/backend/services/data-synthesis-service/pom.xml new file mode 100644 index 0000000..bc146a4 --- /dev/null +++ b/backend/services/data-synthesis-service/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + data-synthesis-service + Data Synthesis Service + 数据合成服务 + + + + com.datamate + domain-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + mysql + mysql-connector-java + ${mysql.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.openapitools + jackson-databind-nullable + + + jakarta.validation + jakarta.validation-api + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + 6.6.0 + + + + generate + + + ${project.basedir}/../../openapi/specs/data-synthesis.yaml + spring + ${project.build.directory}/generated-sources/openapi + com.datamate.synthesis.interfaces.api + com.datamate.synthesis.interfaces.dto + + true + true + true + springdoc + + + + + + + + + + diff --git a/backend/services/execution-engine-service/pom.xml b/backend/services/execution-engine-service/pom.xml new file mode 100644 index 0000000..42f9484 --- /dev/null +++ b/backend/services/execution-engine-service/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + execution-engine-service + Execution Engine Service + 执行引擎服务 + + + + com.datamate + domain-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-redis + + + mysql + mysql-connector-java + ${mysql.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.openapitools + jackson-databind-nullable + + + jakarta.validation + jakarta.validation-api + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + 6.6.0 + + + + generate + + + ${project.basedir}/../../openapi/specs/execution-engine.yaml + spring + ${project.build.directory}/generated-sources/openapi + com.datamate.execution.interfaces.api + com.datamate.execution.interfaces.dto + + true + true + true + springdoc + + + + + + + + + + diff --git a/backend/services/main-application/pom.xml b/backend/services/main-application/pom.xml new file mode 100644 index 0000000..33e4862 --- /dev/null +++ b/backend/services/main-application/pom.xml @@ -0,0 +1,169 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + main-application + jar + Data Mate Platform - Main Application + 主启动应用,集成所有服务模 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + + jakarta.persistence + jakarta.persistence-api + + + + + com.datamate + domain-common + ${project.version} + + + com.datamate + security-common + ${project.version} + + + + org.apache.commons + commons-compress + 1.26.1 + + + + + com.datamate + data-management-service + ${project.version} + + + com.datamate + data-collection-service + ${project.version} + + + com.datamate + operator-market-service + ${project.version} + + + com.datamate + data-cleaning-service + ${project.version} + + + com.datamate + data-synthesis-service + ${project.version} + + + com.datamate + data-annotation-service + ${project.version} + + + com.datamate + data-evaluation-service + ${project.version} + + + com.datamate + pipeline-orchestration-service + ${project.version} + + + com.datamate + execution-engine-service + ${project.version} + + + + + com.datamate + rag-indexer-service + ${project.version} + + + com.datamate + rag-query-service + ${project.version} + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + + mysql + mysql-connector-java + 8.0.33 + runtime + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${maven.compiler.source} + ${maven.compiler.target} + + -parameters + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + data-mate + com.datamate.main.DataMatePlatformApplication + + + + + repackage + + + + + + + + diff --git a/backend/services/main-application/src/main/java/com/datamate/main/DataMatePlatformApplication.java b/backend/services/main-application/src/main/java/com/datamate/main/DataMatePlatformApplication.java new file mode 100644 index 0000000..7267967 --- /dev/null +++ b/backend/services/main-application/src/main/java/com/datamate/main/DataMatePlatformApplication.java @@ -0,0 +1,49 @@ +package com.datamate.main; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * 数据引擎平台主应用 + * 聚合所有业务服务JAR包的微服务启动类 + * + * @author Data Mate Team + * @version 1.0.0 + */ +@SpringBootApplication +@ComponentScan(basePackages = { + "com.datamate.main", + "com.datamate.datamanagement", + "com.datamate.collection", + "com.datamate.operator", + "com.datamate.cleaning", + "com.datamate.synthesis", + "com.datamate.annotation", + "com.datamate.evaluation", + "com.datamate.pipeline", + "com.datamate.execution", + "com.datamate.rag", + "com.datamate.shared", + "com.datamate.common" +}) +@MapperScan(basePackages = { + "com.datamate.collection.infrastructure.persistence.mapper", + "com.datamate.datamanagement.infrastructure.persistence.mapper", + "com.datamate.operator.infrastructure.persistence.mapper", + "com.datamate.cleaning.infrastructure.persistence.mapper", + "com.datamate.common.infrastructure.mapper" +}) +@EnableTransactionManagement +@EnableAsync +@EnableScheduling +public class DataMatePlatformApplication { + + public static void main(String[] args) { + SpringApplication.run(DataMatePlatformApplication.class, args); + } +} diff --git a/backend/services/main-application/src/main/java/com/datamate/main/config/SecurityConfig.java b/backend/services/main-application/src/main/java/com/datamate/main/config/SecurityConfig.java new file mode 100644 index 0000000..f71e8e8 --- /dev/null +++ b/backend/services/main-application/src/main/java/com/datamate/main/config/SecurityConfig.java @@ -0,0 +1,26 @@ +package com.datamate.main.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; + +/** + * 安全配置 - 暂时禁用所有认证 + * 开发阶段使用,生产环境需要启用认证 + */ +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf(csrf -> csrf.disable()) + .authorizeHttpRequests(authz -> authz + .anyRequest().permitAll() // 允许所有请求无需认证 + ); + + return http.build(); + } +} diff --git a/backend/services/main-application/src/main/resources/application.yml b/backend/services/main-application/src/main/resources/application.yml new file mode 100644 index 0000000..9968949 --- /dev/null +++ b/backend/services/main-application/src/main/resources/application.yml @@ -0,0 +1,179 @@ +# 数据引擎平台 - 主应用配置 +spring: + application: + name: data-mate-platform + + # 暂时排除Spring Security自动配置(开发阶段使用) + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration + - org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration + + # 数据源配置 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://mysql:3306/datamate?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: ${DB_USERNAME:root} + password: ${DB_PASSWORD:Huawei@123} + hikari: + maximum-pool-size: 20 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + + # Elasticsearch配置 + elasticsearch: + uris: ${ES_URIS:http://localhost:9200} + username: ${ES_USERNAME:} + password: ${ES_PASSWORD:} + connection-timeout: 10s + socket-timeout: 30s + + # Jackson配置 + jackson: + time-zone: Asia/Shanghai + date-format: yyyy-MM-dd HH:mm:ss + serialization: + write-dates-as-timestamps: false + deserialization: + fail-on-unknown-properties: false + + # 文件上传配置 + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + + # 任务调度配置 + task: + execution: + pool: + core-size: ${TASK_EXECUTION_CORE_SIZE:10} + max-size: ${TASK_EXECUTION_MAX_SIZE:20} + queue-capacity: ${TASK_EXECUTION_QUEUE_CAPACITY:100} + keep-alive: ${TASK_EXECUTION_KEEP_ALIVE:60s} + scheduling: + pool: + size: ${TASK_SCHEDULING_POOL_SIZE:5} + config: + import: + - classpath:config/application-datacollection.yml + - classpath:config/application-datamanagement.yml + +# MyBatis配置(需在顶层,不在 spring 下) +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + default-fetch-size: 100 + default-statement-timeout: 30 + use-generated-keys: true + cache-enabled: true + lazy-loading-enabled: false + multiple-result-sets-enabled: true + use-column-label: true + auto-mapping-behavior: partial + auto-mapping-unknown-column-behavior: none + default-executor-type: simple + call-setters-on-nulls: false + return-instance-for-empty-row: false + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + mapper-locations: + - classpath*:mappers/**/*.xml + +# 应用配置 +server: + port: ${SERVER_PORT:8080} + servlet: + context-path: /api + encoding: + charset: UTF-8 + enabled: true + force: true + +# 日志配置 +logging: + config: classpath:log4j2.xml + +# Actuator配置 +management: + endpoints: + web: + exposure: + include: health,info,metrics,prometheus + endpoint: + health: + show-details: when-authorized + health: + elasticsearch: + enabled: false # 禁用Elasticsearch健康检查 + + +# 平台配置 +datamate: + # JWT配置 + jwt: + secret: ${JWT_SECRET:dataMateSecretKey2024ForJWTTokenGeneration} + expiration: ${JWT_EXPIRATION:86400} # 24小时,单位秒 + header: Authorization + prefix: "Bearer " + + # 文件存储配置 + storage: + type: ${STORAGE_TYPE:local} # local, minio, s3 + local: + base-path: ${STORAGE_LOCAL_PATH:./data/storage} + minio: + endpoint: ${MINIO_ENDPOINT:http://localhost:9000} + access-key: ${MINIO_ACCESS_KEY:minioadmin} + secret-key: ${MINIO_SECRET_KEY:minioadmin} + bucket-name: ${MINIO_BUCKET:data-mate} + + # Ray执行器配置 + ray: + enabled: ${RAY_ENABLED:false} + address: ${RAY_ADDRESS:ray://localhost:10001} + runtime-env: + working-dir: ${RAY_WORKING_DIR:./runtime/python-executor} + pip-packages: + - "ray[default]==2.7.0" + - "pandas" + - "numpy" + - "data-juicer" + + # 数据归集服务配置(可由模块导入叠加) + data-collection: {} + + # 算子市场配置 + operator-market: + repository-path: ${OPERATOR_REPO_PATH:./runtime/operators} + registry-url: ${OPERATOR_REGISTRY_URL:} + max-upload-size: ${OPERATOR_MAX_UPLOAD_SIZE:50MB} + + # 数据处理配置 + data-processing: + max-file-size: ${MAX_FILE_SIZE:1GB} + temp-dir: ${TEMP_DIR:./data/temp} + batch-size: ${BATCH_SIZE:1000} + + # 标注配置 + annotation: + auto-annotation: + enabled: ${AUTO_ANNOTATION_ENABLED:true} + model-endpoint: ${ANNOTATION_MODEL_ENDPOINT:} + quality-control: + enabled: ${QC_ENABLED:true} + threshold: ${QC_THRESHOLD:0.8} + + # RAG配置 + rag: + embedding: + model: ${RAG_EMBEDDING_MODEL:text-embedding-ada-002} + api-key: ${RAG_API_KEY:} + dimension: ${RAG_DIMENSION:1536} + chunk: + size: ${RAG_CHUNK_SIZE:512} + overlap: ${RAG_CHUNK_OVERLAP:50} + retrieval: + top-k: ${RAG_TOP_K:5} + score-threshold: ${RAG_SCORE_THRESHOLD:0.7} diff --git a/backend/services/main-application/src/main/resources/config/application-datacollection.yml b/backend/services/main-application/src/main/resources/config/application-datacollection.yml new file mode 100644 index 0000000..4591655 --- /dev/null +++ b/backend/services/main-application/src/main/resources/config/application-datacollection.yml @@ -0,0 +1,23 @@ +datamate: + data-collection: + # DataX配置 + datax: + home-path: ${DATAX_HOME:/opt/datax} + python-path: ${DATAX_PYTHON_PATH:python3} + job-config-path: ${DATAX_JOB_PATH:./data/temp/datax/jobs} + log-path: ${DATAX_LOG_PATH:./logs/datax} + max-memory: ${DATAX_MAX_MEMORY:2048} + channel-count: ${DATAX_CHANNEL_COUNT:5} + + # 执行配置 + execution: + max-concurrent-tasks: ${DATA_COLLECTION_MAX_CONCURRENT_TASKS:10} + task-timeout-minutes: ${DATA_COLLECTION_TASK_TIMEOUT:120} + retry-count: ${DATA_COLLECTION_RETRY_COUNT:3} + retry-interval-seconds: ${DATA_COLLECTION_RETRY_INTERVAL:30} + + # 监控配置 + monitoring: + status-check-interval-seconds: ${DATA_COLLECTION_STATUS_CHECK_INTERVAL:30} + log-retention-days: ${DATA_COLLECTION_LOG_RETENTION:30} + enable-metrics: ${DATA_COLLECTION_ENABLE_METRICS:true} diff --git a/backend/services/main-application/src/main/resources/config/application-datamanagement.yml b/backend/services/main-application/src/main/resources/config/application-datamanagement.yml new file mode 100644 index 0000000..72fb53d --- /dev/null +++ b/backend/services/main-application/src/main/resources/config/application-datamanagement.yml @@ -0,0 +1,11 @@ +datamate: + datamanagement: + file-storage: + upload-dir: ${FILE_UPLOAD_DIR:./uploads} + max-file-size: 10485760 # 10MB + max-request-size: 52428800 # 50MB + cache: + ttl: 3600 + max-size: 1000 +# MyBatis is configured centrally in main-application (mapper-locations & aliases) +# to avoid list overriding issues when importing multiple module configs. diff --git a/backend/services/main-application/src/main/resources/log4j2.xml b/backend/services/main-application/src/main/resources/log4j2.xml new file mode 100644 index 0000000..f9d0cf3 --- /dev/null +++ b/backend/services/main-application/src/main/resources/log4j2.xml @@ -0,0 +1,42 @@ + + + + /var/log/data-mate/backend + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n + 100MB + 30 + INFO + WARN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/services/operator-market-service/img.png b/backend/services/operator-market-service/img.png new file mode 100644 index 0000000000000000000000000000000000000000..a8ec1f263fbb4b86d46d818b85a53ee852803d00 GIT binary patch literal 146785 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 literal 0 HcmV?d00001 diff --git a/backend/services/operator-market-service/pom.xml b/backend/services/operator-market-service/pom.xml new file mode 100644 index 0000000..6543a1e --- /dev/null +++ b/backend/services/operator-market-service/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + operator-market-service + Operator Market Service + 算子市场服务 + + + + com.datamate + domain-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-redis + + + mysql + mysql-connector-java + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.openapitools + jackson-databind-nullable + + + jakarta.validation + jakarta.validation-api + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + org.projectlombok + lombok + provided + + + org.apache.commons + commons-compress + 1.26.1 + + + + org.mapstruct + mapstruct + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/OperatorMarketServiceConfiguration.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/OperatorMarketServiceConfiguration.java new file mode 100644 index 0000000..e05a2bd --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/OperatorMarketServiceConfiguration.java @@ -0,0 +1,24 @@ +package com.datamate.operator; + +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * Operator Market Service Configuration + * 算子市场服务配置类 - 版本、安装、评分、仓库 + */ +@Configuration +@EnableAsync +@EnableScheduling +@EntityScan(basePackages = "com.datamate.operator.domain.modal") +@ComponentScan(basePackages = { + "com.datamate.operator", + "com.datamate.shared" +}) +public class OperatorMarketServiceConfiguration { + // Service configuration class for JAR packaging + // 作为jar包形式提供服务的配置类 +} diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java new file mode 100644 index 0000000..45da8d8 --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/CategoryService.java @@ -0,0 +1,62 @@ +package com.datamate.operator.application; + + +import com.datamate.operator.domain.modal.Category; +import com.datamate.operator.domain.modal.CategoryRelation; +import com.datamate.operator.infrastructure.persistence.mapper.CategoryMapper; +import com.datamate.operator.infrastructure.persistence.mapper.CategoryRelationMapper; +import com.datamate.operator.interfaces.dto.CategoryTreeResponse; +import com.datamate.operator.interfaces.dto.SubCategory; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CategoryService { + private final CategoryMapper categoryMapper; + + private final CategoryRelationMapper categoryRelationMapper; + + public List getAllCategories() { + List allCategories = categoryMapper.findAllCategories(); + List allRelations = categoryRelationMapper.findAllRelation(); + + Map relationMap = allRelations.stream() + .collect(Collectors.groupingBy( + CategoryRelation::getCategoryId, + Collectors.collectingAndThen(Collectors.counting(), Math::toIntExact))); + + Map nameMap = allCategories.stream() + .collect(Collectors.toMap(Category::getId, Category::getName)); + Map> groupedByParentId = allCategories.stream() + .filter(relation -> relation.getParentId() > 0) + .collect(Collectors.groupingBy(Category::getParentId)); + + return groupedByParentId.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(entry -> { + Integer parentId = entry.getKey(); + List group = entry.getValue(); + CategoryTreeResponse response = new CategoryTreeResponse(); + response.setId(parentId); + response.setName(nameMap.get(parentId)); + AtomicInteger totalCount = new AtomicInteger(); + response.setCategories(group.stream().map(category -> { + SubCategory subCategory = new SubCategory(); + subCategory.setId(category.getId()); + subCategory.setName(category.getName()); + subCategory.setCount(relationMap.getOrDefault(category.getId(), 0)); + totalCount.getAndAdd(relationMap.getOrDefault(category.getId(), 0)); + subCategory.setParentId(parentId); + return subCategory; + }).toList()); + response.setCount(totalCount.get()); + return response; + }).toList(); + } +} diff --git a/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/LabelService.java b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/LabelService.java new file mode 100644 index 0000000..cba6ce4 --- /dev/null +++ b/backend/services/operator-market-service/src/main/java/com/datamate/operator/application/LabelService.java @@ -0,0 +1,22 @@ +package com.datamate.operator.application; + +import com.datamate.operator.interfaces.dto.Label; +import com.datamate.operator.interfaces.dto.*; +import org.springframework.stereotype.Service; +import java.util.List; +import java.util.Collections; + +@Service +public class LabelService { + public List

+ * 在使用全局响应包装时,如果某个接口或类不需要进行响应包装,可以使用此注解进行标记 + *

+ */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface IgnoreResponseWrap { +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/common/Response.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/common/Response.java new file mode 100644 index 0000000..2451fe6 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/common/Response.java @@ -0,0 +1,63 @@ +package com.datamate.common.infrastructure.common; + +import com.datamate.common.infrastructure.exception.ErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 通用返回体 + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Response implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 状态码 + */ + private String code; + + /** + * 消息 + */ + private String message; + + /** + * 数据 + */ + private T data; + + /** + * 构造成功时的返回体 + * + * @param data 返回数据 + * @param 返回数据类型 + * @return 返回体内容 + */ + public static Response ok(T data) { + return new Response<>("0", "success", data); + } + + /** + * 构造错误时的返回体 + * + * @param errorCode 错误码 + * @param data 返回数据 + * @param 返回数据类型 + * @return 返回体内容 + */ + public static Response error(ErrorCode errorCode, T data) { + return new Response<>(errorCode.getCode(), errorCode.getMessage(), data); + } + + public static Response error(ErrorCode errorCode) { + return new Response<>(errorCode.getCode(), errorCode.getMessage(), null); + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/EntityMetaObjectHandler.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/EntityMetaObjectHandler.java new file mode 100644 index 0000000..47b2ed8 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/EntityMetaObjectHandler.java @@ -0,0 +1,60 @@ +package com.datamate.common.infrastructure.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.context.annotation.Configuration; + +import java.time.LocalDateTime; + +/** + * 持久化实体元数据对象处理器 + * + * @author dallas + * @since 2025-10-17 + */ +@Slf4j +@Configuration +public class EntityMetaObjectHandler implements MetaObjectHandler { + @Override + public void insertFill(MetaObject metaObject) { + log.debug("Starting insert fill..."); + + // 创建时间填充 + this.strictInsertFill(metaObject, "createdAt", LocalDateTime.class, LocalDateTime.now()); + // 更新时间填充 + this.strictInsertFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now()); + // 创建人填充(需要从安全上下文获取当前用户) + String currentUser = getCurrentUser(); + this.strictInsertFill(metaObject, "createdBy", String.class, currentUser); + // 更新人填充 + this.strictInsertFill(metaObject, "updatedBy", String.class, currentUser); + } + + @Override + public void updateFill(MetaObject metaObject) { + log.debug("Starting update fill..."); + // 更新时间填充 + this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now()); + // 更新人填充 + this.strictUpdateFill(metaObject, "updatedBy", String.class, getCurrentUser()); + } + + /** + * 获取当前用户(需要根据你的安全框架实现) + */ + private String getCurrentUser() { + // todo 这里需要根据你的安全框架实现,例如Spring Security、Shiro等 + // 示例:返回默认用户或从SecurityContext获取 + try { + // 如果是Spring Security + // return SecurityContextHolder.getContext().getAuthentication().getName(); + + // 临时返回默认值,请根据实际情况修改 + return "system"; + } catch (Exception e) { + log.error("Error getting current user", e); + return "unknown"; + } + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalExceptionHandler.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..bc49c50 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalExceptionHandler.java @@ -0,0 +1,54 @@ +package com.datamate.common.infrastructure.config; + +import com.datamate.common.infrastructure.common.Response; +import com.datamate.common.infrastructure.exception.BusinessException; +import com.datamate.common.infrastructure.exception.SystemErrorCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.stream.Collectors; + +/** + * + * + * @author dallas + * @since 2025-10-17 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + /** + * 处理自定义业务异常 + */ + @ExceptionHandler(BusinessException.class) + public ResponseEntity> handleBusinessException(BusinessException e) { + log.warn("BusinessException: code={}, message={}", e.getCode(), e.getMessage(), e); + return ResponseEntity.internalServerError().body(Response.error(e.getErrorCodeEnum())); + } + + /** + * 处理参数校验和数据绑定异常 + */ + @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class}) + public ResponseEntity> handleMethodArgumentNotValidException(BindException e) { + String message = e.getBindingResult().getFieldErrors().stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining(", ")); + log.warn("Parameter validation failed: {}", message); + return ResponseEntity.badRequest().body(Response.error(SystemErrorCode.INVALID_PARAMETER, message)); + } + + /** + * 处理系统兜底异常 + */ + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException(Exception e) { + log.error("SystemException: ", e); + return ResponseEntity.internalServerError().body(Response.error(SystemErrorCode.SYSTEM_BUSY)); + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalResponseHandler.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalResponseHandler.java new file mode 100644 index 0000000..c96698e --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/GlobalResponseHandler.java @@ -0,0 +1,77 @@ +package com.datamate.common.infrastructure.config; + +import com.datamate.common.infrastructure.common.IgnoreResponseWrap; +import com.datamate.common.infrastructure.common.Response; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +/** + * 全局响应处理器 + * + * @author dallas + * @since 2025-10-17 + */ +@Slf4j +@RequiredArgsConstructor +@RestControllerAdvice(basePackages = "com.datamate") +public class GlobalResponseHandler implements ResponseBodyAdvice { + private final ObjectMapper objectMapper; + + /** + * 判断哪些返回值需要被包装。 + * 返回true表示执行beforeBodyWrite方法。 + */ + @Override + public boolean supports(MethodParameter returnType, @Nullable Class converterType) { + // 1. 如果返回类型已经是Response,直接跳过包装 + if (returnType.getParameterType().isAssignableFrom(Response.class)) { + return false; + } + // 2. 检查方法或所在类上是否有@IgnoreResponseWrap的注解,如果有,返回false + if (returnType.hasMethodAnnotation(IgnoreResponseWrap.class) || + returnType.getContainingClass().isAnnotationPresent(IgnoreResponseWrap.class)) { + return false; + } + // 3. 默认情况下,对其他返回类型进行包装 + return true; + } + + /** + * 对响应体进行实际包装处理。 + */ + @Override + public Object beforeBodyWrite(Object body, + @Nullable MethodParameter returnType, + @Nullable MediaType selectedContentType, + @Nullable Class selectedConverterType, + @Nullable ServerHttpRequest request, + @Nullable ServerHttpResponse response) { + // 如果返回体本身就是Response类型(通常来自异常处理),直接返回 + if (body instanceof Response) { + return body; + } + + // 如果返回值是String类型,需要特殊处理 + if (body instanceof String) { + // 手动将Response对象序列化为JSON字符串返回 + try { + return objectMapper.writeValueAsString(Response.ok(body)); + } catch (JsonProcessingException e) { + // 记录日志或抛出运行时异常 + log.error("Error serializing response", e); + throw new RuntimeException("Error converting response to JSON", e); + } + } + // 对于正常的返回结果,统一包装成成功的Response + return Response.ok(body); + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/MybatisPlusConfig.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/MybatisPlusConfig.java new file mode 100644 index 0000000..f5e1e11 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/config/MybatisPlusConfig.java @@ -0,0 +1,47 @@ +package com.datamate.common.infrastructure.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +/** + * Mybatis Plus 配置类 + * + * @author dallas + * @since 2025-10-20 + */ +@Configuration +public class MybatisPlusConfig { + /** + * 配置 JacksonTypeHandler 以支持 Java 8 日期时间类型 + */ + @Bean + @Primary + public JacksonTypeHandler jacksonTypeHandler() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + JacksonTypeHandler handler = new JacksonTypeHandler(Object.class); + JacksonTypeHandler.setObjectMapper(objectMapper); + return handler; + } + + /** + * 配置 Mybatis Plus 分页插件 + * + * @return MybatisPlusInterceptor + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessAssert.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessAssert.java new file mode 100644 index 0000000..ec69c6b --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessAssert.java @@ -0,0 +1,35 @@ +package com.datamate.common.infrastructure.exception; + +import java.util.Collection; + +/** + * 断言工具类,用于在业务逻辑中快速检查条件是否满足,不满足时抛出业务异常 + * + * @author dallas + * @since 2025-10-17 + */ +public class BusinessAssert { + public static void isTrue(boolean condition, ErrorCode errorCode) { + if (!condition) { + throw BusinessException.of(errorCode); + } + } + + public static void notNull(Object obj, ErrorCode errorCode) { + if (obj == null) { + throw BusinessException.of(errorCode); + } + } + + public static void notEmpty(Collection collection, ErrorCode errorCode) { + if (collection == null || collection.isEmpty()) { + throw BusinessException.of(errorCode); + } + } + + public static void isTrue(boolean condition, ErrorCode errorCode, String customMessage) { + if (!condition) { + throw BusinessException.of(errorCode, customMessage); + } + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessException.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessException.java new file mode 100644 index 0000000..509c646 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/BusinessException.java @@ -0,0 +1,73 @@ +package com.datamate.common.infrastructure.exception; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * 业务异常基类 + * + * @author dallas + * @since 2025-10-15 + */ +public class BusinessException extends RuntimeException { + private final ErrorCode errorCode; + private final Map details; + + // 核心构造方法 + private BusinessException(ErrorCode errorCode, String customMessage, + Map details, Throwable cause) { + super(customMessage != null ? customMessage : errorCode.getMessage(), cause); + this.errorCode = errorCode; + this.details = details != null ? new HashMap<>(details) : new HashMap<>(); + } + + // 静态工厂方法 + public static BusinessException of(ErrorCode errorCode) { + return new BusinessException(errorCode, null, null, null); + } + + public static BusinessException of(ErrorCode errorCode, String customMessage) { + return new BusinessException(errorCode, customMessage, null, null); + } + + public static BusinessException of(ErrorCode errorCode, Map details) { + return new BusinessException(errorCode, null, details, null); + } + + public static BusinessException of(ErrorCode errorCode, String customMessage, + Map details) { + return new BusinessException(errorCode, customMessage, details, null); + } + + // 快速创建方法 - 支持链式调用添加详情 + public static BusinessException create(ErrorCode errorCode) { + return new BusinessException(errorCode, null, null, null); + } + + public BusinessException withDetail(String key, Object value) { + this.details.put(key, value); + return this; + } + + public BusinessException withCustomMessage(String customMessage) { + return new BusinessException(this.errorCode, customMessage, this.details, null); + } + + // Getter方法 + public String getCode() { + return errorCode.getCode(); + } + + public ErrorCode getErrorCodeEnum() { + return errorCode; + } + + public Map getDetails() { + return Collections.unmodifiableMap(details); + } + + public String getOriginalMessage() { + return errorCode.getMessage(); + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCode.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCode.java new file mode 100644 index 0000000..2b9f71f --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCode.java @@ -0,0 +1,23 @@ +package com.datamate.common.infrastructure.exception; + +/** + * 错误码接口 + * + * @author dallas + * @since 2025-10-17 + */ +public interface ErrorCode { + /** + * 获取错误码 + * + * @return 错误码 + */ + String getCode(); + + /** + * 获取错误信息 + * + * @return 错误信息 + */ + String getMessage(); +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCodeImpl.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCodeImpl.java new file mode 100644 index 0000000..5931f05 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/ErrorCodeImpl.java @@ -0,0 +1,15 @@ +package com.datamate.common.infrastructure.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class ErrorCodeImpl implements ErrorCode { + private final String code; + private final String message; + + public static ErrorCodeImpl of(String code, String message) { + return new ErrorCodeImpl(code, message); + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/SystemErrorCode.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/SystemErrorCode.java new file mode 100644 index 0000000..8677f73 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/exception/SystemErrorCode.java @@ -0,0 +1,43 @@ +package com.datamate.common.infrastructure.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 系统错误码枚举 + * + * @author dallas + * @since 2025-10-17 + */ +@Getter +@AllArgsConstructor +public enum SystemErrorCode implements ErrorCode { + /** + * 未知错误 + */ + UNKNOWN_ERROR("sys.0001", "未知错误"), + /** + * 系统繁忙,请稍后重试 + */ + SYSTEM_BUSY("sys.0002", "系统繁忙,请稍后重试"), + /** + * 参数错误 + */ + INVALID_PARAMETER("sys.0003", "参数错误"), + /** + * 资源未找到 + */ + RESOURCE_NOT_FOUND("sys.0004", "资源未找到"), + /** + * 权限不足 + */ + INSUFFICIENT_PERMISSIONS("sys.0005", "权限不足"), + + /** + * 文件系统错误 + */ + FILE_SYSTEM_ERROR("sys.0006", "文件系统错误"); + + private final String code; + private final String message; +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/mapper/ChunkUploadRequestMapper.java b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/mapper/ChunkUploadRequestMapper.java new file mode 100644 index 0000000..4722aed --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/infrastructure/mapper/ChunkUploadRequestMapper.java @@ -0,0 +1,49 @@ +package com.datamate.common.infrastructure.mapper; + +import com.datamate.common.domain.model.ChunkUploadPreRequest; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 文件切片上传请求Mapper + */ +@Mapper +public interface ChunkUploadRequestMapper { + + /** + * 根据ID查询 + */ + ChunkUploadPreRequest findById(@Param("id") String id); + + /** + * 根据服务ID查询 + */ + List findByServiceId(@Param("serviceId") String serviceId); + + /** + * 查询所有 + */ + List findAll(); + + /** + * 插入 + */ + int insert(ChunkUploadPreRequest request); + + /** + * 更新 + */ + int update(ChunkUploadPreRequest request); + + /** + * 根据ID删除 + */ + int deleteById(@Param("id") String id); + + /** + * 根据服务ID删除 + */ + int deleteByServiceId(@Param("serviceId") String serviceId); +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagedResponse.java b/backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagedResponse.java new file mode 100644 index 0000000..7d87328 --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagedResponse.java @@ -0,0 +1,44 @@ +package com.datamate.common.interfaces; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class PagedResponse { + private long page; + private long size; + private long totalElements; + private long totalPages; + private List content; + + public PagedResponse(List content) { + this.page = 0; + this.size = content.size(); + this.totalElements = content.size(); + this.totalPages = 1; + this.content = content; + } + + public PagedResponse(List content, long page, long totalElements, long totalPages) { + this.page = page; + this.size = content.size(); + this.totalElements = totalElements; + this.totalPages = totalPages; + this.content = content; + } + + public static PagedResponse of(List content) { + return new PagedResponse<>(content); + } + + public static PagedResponse of(List content, long page, long totalElements, long totalPages) { + return new PagedResponse<>(content, page, totalElements, totalPages); + } +} diff --git a/backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagingQuery.java b/backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagingQuery.java new file mode 100644 index 0000000..798075f --- /dev/null +++ b/backend/shared/domain-common/src/main/java/com/datamate/common/interfaces/PagingQuery.java @@ -0,0 +1,22 @@ +package com.datamate.common.interfaces; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class PagingQuery { + /** + * 页码,从0开始 + */ + private Integer page = 0; + + /** + * 每页大小 + */ + private Integer size = 20; +} diff --git a/backend/shared/domain-common/src/main/resources/mappers/ChunkUploadRequestMapper.xml b/backend/shared/domain-common/src/main/resources/mappers/ChunkUploadRequestMapper.xml new file mode 100644 index 0000000..53c31cc --- /dev/null +++ b/backend/shared/domain-common/src/main/resources/mappers/ChunkUploadRequestMapper.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + id, total_file_num, uploaded_file_num, upload_path, timeout, service_id, check_info + + + + + + + + + + INSERT INTO t_chunk_upload_request ( + id, total_file_num, uploaded_file_num, upload_path, timeout, service_id, check_info + ) VALUES ( + #{id}, #{totalFileNum}, #{uploadedFileNum}, #{uploadPath}, #{timeout}, #{serviceId}, #{checkInfo} + ) + + + + UPDATE t_chunk_upload_request + SET total_file_num = #{totalFileNum}, + uploaded_file_num = #{uploadedFileNum}, + upload_path = #{uploadPath}, + timeout = #{timeout}, + service_id = #{serviceId}, + check_info = #{checkInfo} + WHERE id = #{id} + + + + DELETE FROM t_chunk_upload_request WHERE id = #{id} + + + + DELETE FROM t_chunk_upload_request WHERE service_id = #{serviceId} + + diff --git a/backend/shared/security-common/pom.xml b/backend/shared/security-common/pom.xml new file mode 100644 index 0000000..686234b --- /dev/null +++ b/backend/shared/security-common/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + + com.datamate + data-mate-platform + 1.0.0-SNAPSHOT + ../../pom.xml + + + security-common + Security Common + 安全通用组件 + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + diff --git a/backend/shared/security-common/src/main/java/com/datamate/common/security/JwtUtils.java b/backend/shared/security-common/src/main/java/com/datamate/common/security/JwtUtils.java new file mode 100644 index 0000000..efe4a4b --- /dev/null +++ b/backend/shared/security-common/src/main/java/com/datamate/common/security/JwtUtils.java @@ -0,0 +1,106 @@ +package com.datamate.common.security; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * JWT工具类 + */ +@Component +public class JwtUtils { + + @Value("${jwt.secret:datamate-secret-key-for-jwt-token-generation}") + private String secret; + + @Value("${jwt.expiration:86400}") // 24小时 + private Long expiration; + + private SecretKey getSigningKey() { + return Keys.hmacShaKeyFor(secret.getBytes()); + } + + /** + * 生成JWT令牌 + */ + public String generateToken(String username, Map claims) { + Map tokenClaims = new HashMap<>(); + if (claims != null) { + tokenClaims.putAll(claims); + } + tokenClaims.put("sub", username); + + return Jwts.builder() + .setClaims(tokenClaims) + .setSubject(username) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) + .signWith(getSigningKey(), SignatureAlgorithm.HS512) + .compact(); + } + + /** + * 从令牌中获取用户名 + */ + public String getUsernameFromToken(String token) { + return getClaimsFromToken(token).getSubject(); + } + + /** + * 从令牌中获取过期时间 + */ + public Date getExpirationDateFromToken(String token) { + return getClaimsFromToken(token).getExpiration(); + } + + /** + * 从令牌中获取声明 + */ + public Claims getClaimsFromToken(String token) { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + /** + * 验证令牌是否过期 + */ + public Boolean isTokenExpired(String token) { + Date expiration = getExpirationDateFromToken(token); + return expiration.before(new Date()); + } + + /** + * 验证令牌 + */ + public Boolean validateToken(String token, String username) { + try { + String tokenUsername = getUsernameFromToken(token); + return (username.equals(tokenUsername) && !isTokenExpired(token)); + } catch (JwtException | IllegalArgumentException e) { + return false; + } + } + + /** + * 刷新令牌 + */ + public String refreshToken(String token) { + Claims claims = getClaimsFromToken(token); + claims.setIssuedAt(new Date()); + claims.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)); + + return Jwts.builder() + .setClaims(claims) + .signWith(getSigningKey(), SignatureAlgorithm.HS512) + .compact(); + } +} diff --git a/deployment/docker/datamate/docker-compose.yml b/deployment/docker/datamate/docker-compose.yml new file mode 100644 index 0000000..de123eb --- /dev/null +++ b/deployment/docker/datamate/docker-compose.yml @@ -0,0 +1,86 @@ +services: + # 1) backend + backend: + container_name: backend + image: backend + restart: on-failure + privileged: true + ports: + - "8080:8080" + volumes: + - dataset_volume:/dataset + - flow_volume:/flow + - log_volume:/var/log/data-mate + networks: [ edatamate ] + depends_on: + - mysql + + # 2) frontend(NodePort 30000) + frontend: + container_name: frontend + image: frontend + restart: on-failure + ports: + - "30000:80" # nodePort → hostPort + volumes: + - log_volume:/var/log/data-mate + networks: [ edatamate ] + depends_on: + - backend + + # 3) mysql + mysql: + container_name: mysql + image: mysql:8 + restart: on-failure + environment: + MYSQL_ROOT_PASSWORD: Huawei@123 + ports: + - "3306:3306" + volumes: + - mysql_volume:/var/lib/mysql + - ../../../scripts/db:/docker-entrypoint-initdb.d + - ./utf8.cnf:/etc/mysql/conf.d/utf8.cnf + - log_volume:/var/log/data-mate + networks: [ edatamate ] + + runtime: + container_name: runtime + image: runtime + restart: on-failure + environment: + RAY_DEDUP_LOGS: "0" + RAY_TQDM_PATCH_PRINT: "0" + MYSQL_HOST: "mysql" + MYSQL_PORT: "3306" + MYSQL_USER: "root" + MYSQL_PASSWORD: "Huawei@123" + MYSQL_DATABASE: "datamate" + ports: + - "8081:8081" + command: + - python + - /opt/runtime/datamate/operator_runtime.py + - --port + - "8081" + volumes: + - ray_log_volume:/tmp/ray + - log_volume:/var/log/data-mate + - dataset_volume:/dataset + - flow_volume:/flow + +volumes: + dataset_volume: + name: data-mate-dataset-volume + flow_volume: + name: data-mate-flow-volume + log_volume: + name: data-mate-log-volume + mysql_volume: + name: data-mate-mysql-volume + ray_log_volume: + name: data-mate-ray-log-volume + +networks: + edatamate: + driver: bridge diff --git a/deployment/docker/datamate/utf8.cnf b/deployment/docker/datamate/utf8.cnf new file mode 100644 index 0000000..4543673 --- /dev/null +++ b/deployment/docker/datamate/utf8.cnf @@ -0,0 +1,15 @@ +[mysqld] +# 设置服务器默认字符集为 utf8mb4 (推荐,支持完整的 UTF-8,包括 emoji) +character-set-server = utf8mb4 +# 设置默认排序规则 +collation-server = utf8mb4_unicode_ci +# 或者使用 utf8_general_ci (性能稍好,但排序规则稍宽松) +default-time-zone = 'Asia/Shanghai' + +[client] +# 设置客户端连接默认字符集 +default-character-set = utf8mb4 + +[mysql] +# 设置 mysql 命令行客户端默认字符集 +default-character-set = utf8mb4 \ No newline at end of file diff --git a/deployment/helm/ray/kuberay-operator/Chart.yaml b/deployment/helm/ray/kuberay-operator/Chart.yaml new file mode 100644 index 0000000..8e8f770 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 + +name: kuberay-operator + +description: A Helm chart for deploying the Kuberay operator on Kubernetes. + +version: 1.4.2 + +type: application + +keywords: +- ray +- ray operator +- distributed computing +- data processing +- machine learning +- deep learning +- hyperparameter tuning +- reinforcement learning +- model serving + +home: https://github.com/ray-project/kuberay + +icon: https://github.com/ray-project/ray/raw/master/doc/source/images/ray_header_logo.png diff --git a/deployment/helm/ray/kuberay-operator/crds/ray.io_rayclusters.yaml b/deployment/helm/ray/kuberay-operator/crds/ray.io_rayclusters.yaml new file mode 100644 index 0000000..a8e1f1d --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/crds/ray.io_rayclusters.yaml @@ -0,0 +1,16101 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: rayclusters.ray.io +spec: + group: ray.io + names: + categories: + - all + kind: RayCluster + listKind: RayClusterList + plural: rayclusters + singular: raycluster + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.desiredWorkerReplicas + name: desired workers + type: integer + - jsonPath: .status.availableWorkerReplicas + name: available workers + type: integer + - jsonPath: .status.desiredCPU + name: cpus + type: string + - jsonPath: .status.desiredMemory + name: memory + type: string + - jsonPath: .status.desiredGPU + name: gpus + type: string + - jsonPath: .status.desiredTPU + name: tpus + priority: 1 + type: string + - jsonPath: .status.state + name: status + type: string + - jsonPath: .metadata.creationTimestamp + name: age + type: date + - jsonPath: .status.head.podIP + name: head pod IP + priority: 1 + type: string + - jsonPath: .status.head.serviceIP + name: head service IP + priority: 1 + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autoscalerOptions: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + idleTimeoutSeconds: + format: int32 + type: integer + image: + type: string + imagePullPolicy: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + upscalingMode: + enum: + - Default + - Aggressive + - Conservative + type: string + version: + enum: + - v1 + - v2 + type: string + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + type: object + enableInTreeAutoscaling: + type: boolean + gcsFaultToleranceOptions: + properties: + externalStorageNamespace: + type: string + redisAddress: + type: string + redisPassword: + properties: + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + type: object + redisUsername: + properties: + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + type: object + required: + - redisAddress + type: object + headGroupSpec: + properties: + enableIngress: + type: boolean + headService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + rayStartParams: + additionalProperties: + type: string + type: object + serviceType: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - template + type: object + headServiceAnnotations: + additionalProperties: + type: string + type: object + managedBy: + type: string + x-kubernetes-validations: + - message: the managedBy field is immutable + rule: self == oldSelf + - message: the managedBy field value must be either 'ray.io/kuberay-operator' + or 'kueue.x-k8s.io/multikueue' + rule: self in ['ray.io/kuberay-operator', 'kueue.x-k8s.io/multikueue'] + rayVersion: + type: string + suspend: + type: boolean + workerGroupSpecs: + items: + properties: + groupName: + type: string + idleTimeoutSeconds: + format: int32 + type: integer + maxReplicas: + default: 2147483647 + format: int32 + type: integer + minReplicas: + default: 0 + format: int32 + type: integer + numOfHosts: + default: 1 + format: int32 + type: integer + rayStartParams: + additionalProperties: + type: string + type: object + replicas: + default: 0 + format: int32 + type: integer + scaleStrategy: + properties: + workersToDelete: + items: + type: string + type: array + type: object + suspend: + type: boolean + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - groupName + - maxReplicas + - minReplicas + - template + type: object + type: array + required: + - headGroupSpec + type: object + status: + properties: + availableWorkerReplicas: + format: int32 + type: integer + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + podName: + type: string + serviceIP: + type: string + serviceName: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + stateTransitionTimes: + additionalProperties: + format: date-time + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.desiredWorkerReplicas + name: desired workers + type: integer + - jsonPath: .status.availableWorkerReplicas + name: available workers + type: integer + - jsonPath: .status.desiredCPU + name: cpus + type: string + - jsonPath: .status.desiredMemory + name: memory + type: string + - jsonPath: .status.desiredGPU + name: gpus + type: string + - jsonPath: .status.desiredTPU + name: tpus + priority: 1 + type: string + - jsonPath: .status.state + name: status + type: string + - jsonPath: .metadata.creationTimestamp + name: age + type: date + - jsonPath: .status.head.podIP + name: head pod IP + priority: 1 + type: string + - jsonPath: .status.head.serviceIP + name: head service IP + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + autoscalerOptions: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + idleTimeoutSeconds: + format: int32 + type: integer + image: + type: string + imagePullPolicy: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + upscalingMode: + enum: + - Default + - Aggressive + - Conservative + type: string + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + type: object + enableInTreeAutoscaling: + type: boolean + headGroupSpec: + properties: + enableIngress: + type: boolean + headService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + rayStartParams: + additionalProperties: + type: string + type: object + serviceType: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - rayStartParams + - template + type: object + headServiceAnnotations: + additionalProperties: + type: string + type: object + rayVersion: + type: string + suspend: + type: boolean + workerGroupSpecs: + items: + properties: + groupName: + type: string + maxReplicas: + default: 2147483647 + format: int32 + type: integer + minReplicas: + default: 0 + format: int32 + type: integer + rayStartParams: + additionalProperties: + type: string + type: object + replicas: + default: 0 + format: int32 + type: integer + scaleStrategy: + properties: + workersToDelete: + items: + type: string + type: array + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - groupName + - maxReplicas + - minReplicas + - rayStartParams + - template + type: object + type: array + required: + - headGroupSpec + type: object + status: + properties: + availableWorkerReplicas: + format: int32 + type: integer + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + serviceIP: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/deployment/helm/ray/kuberay-operator/crds/ray.io_rayjobs.yaml b/deployment/helm/ray/kuberay-operator/crds/ray.io_rayjobs.yaml new file mode 100644 index 0000000..f3dc33c --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/crds/ray.io_rayjobs.yaml @@ -0,0 +1,23549 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: rayjobs.ray.io +spec: + group: ray.io + names: + categories: + - all + kind: RayJob + listKind: RayJobList + plural: rayjobs + singular: rayjob + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.jobStatus + name: job status + type: string + - jsonPath: .status.jobDeploymentStatus + name: deployment status + type: string + - jsonPath: .status.rayClusterName + name: ray cluster name + type: string + - jsonPath: .status.startTime + name: start time + type: string + - jsonPath: .status.endTime + name: end time + type: string + - jsonPath: .metadata.creationTimestamp + name: age + type: date + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + activeDeadlineSeconds: + format: int32 + type: integer + backoffLimit: + default: 0 + format: int32 + type: integer + clusterSelector: + additionalProperties: + type: string + type: object + deletionPolicy: + type: string + x-kubernetes-validations: + - message: the deletionPolicy field value must be either 'DeleteCluster', + 'DeleteWorkers', 'DeleteSelf', or 'DeleteNone' + rule: self in ['DeleteCluster', 'DeleteWorkers', 'DeleteSelf', 'DeleteNone'] + entrypoint: + type: string + entrypointNumCpus: + type: number + entrypointNumGpus: + type: number + entrypointResources: + type: string + jobId: + type: string + managedBy: + type: string + x-kubernetes-validations: + - message: the managedBy field is immutable + rule: self == oldSelf + - message: the managedBy field value must be either 'ray.io/kuberay-operator' + or 'kueue.x-k8s.io/multikueue' + rule: self in ['ray.io/kuberay-operator', 'kueue.x-k8s.io/multikueue'] + metadata: + additionalProperties: + type: string + type: object + rayClusterSpec: + properties: + autoscalerOptions: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + idleTimeoutSeconds: + format: int32 + type: integer + image: + type: string + imagePullPolicy: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + upscalingMode: + enum: + - Default + - Aggressive + - Conservative + type: string + version: + enum: + - v1 + - v2 + type: string + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + type: object + enableInTreeAutoscaling: + type: boolean + gcsFaultToleranceOptions: + properties: + externalStorageNamespace: + type: string + redisAddress: + type: string + redisPassword: + properties: + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + type: object + redisUsername: + properties: + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + type: object + required: + - redisAddress + type: object + headGroupSpec: + properties: + enableIngress: + type: boolean + headService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + rayStartParams: + additionalProperties: + type: string + type: object + serviceType: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - template + type: object + headServiceAnnotations: + additionalProperties: + type: string + type: object + managedBy: + type: string + x-kubernetes-validations: + - message: the managedBy field is immutable + rule: self == oldSelf + - message: the managedBy field value must be either 'ray.io/kuberay-operator' + or 'kueue.x-k8s.io/multikueue' + rule: self in ['ray.io/kuberay-operator', 'kueue.x-k8s.io/multikueue'] + rayVersion: + type: string + suspend: + type: boolean + workerGroupSpecs: + items: + properties: + groupName: + type: string + idleTimeoutSeconds: + format: int32 + type: integer + maxReplicas: + default: 2147483647 + format: int32 + type: integer + minReplicas: + default: 0 + format: int32 + type: integer + numOfHosts: + default: 1 + format: int32 + type: integer + rayStartParams: + additionalProperties: + type: string + type: object + replicas: + default: 0 + format: int32 + type: integer + scaleStrategy: + properties: + workersToDelete: + items: + type: string + type: array + type: object + suspend: + type: boolean + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - groupName + - maxReplicas + - minReplicas + - template + type: object + type: array + required: + - headGroupSpec + type: object + runtimeEnvYAML: + type: string + shutdownAfterJobFinishes: + type: boolean + submissionMode: + default: K8sJobMode + type: string + submitterConfig: + properties: + backoffLimit: + format: int32 + type: integer + type: object + submitterPodTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + suspend: + type: boolean + ttlSecondsAfterFinished: + default: 0 + format: int32 + type: integer + type: object + status: + properties: + dashboardURL: + type: string + endTime: + format: date-time + type: string + failed: + default: 0 + format: int32 + type: integer + jobDeploymentStatus: + type: string + jobId: + type: string + jobStatus: + type: string + message: + type: string + observedGeneration: + format: int64 + type: integer + rayClusterName: + type: string + rayClusterStatus: + properties: + availableWorkerReplicas: + format: int32 + type: integer + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + podName: + type: string + serviceIP: + type: string + serviceName: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + stateTransitionTimes: + additionalProperties: + format: date-time + type: string + type: object + type: object + rayJobInfo: + properties: + endTime: + format: date-time + type: string + startTime: + format: date-time + type: string + type: object + reason: + type: string + startTime: + format: date-time + type: string + succeeded: + default: 0 + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + clusterSelector: + additionalProperties: + type: string + type: object + entrypoint: + type: string + entrypointNumCpus: + type: number + entrypointNumGpus: + type: number + entrypointResources: + type: string + jobId: + type: string + metadata: + additionalProperties: + type: string + type: object + rayClusterSpec: + properties: + autoscalerOptions: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + idleTimeoutSeconds: + format: int32 + type: integer + image: + type: string + imagePullPolicy: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + upscalingMode: + enum: + - Default + - Aggressive + - Conservative + type: string + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + type: object + enableInTreeAutoscaling: + type: boolean + headGroupSpec: + properties: + enableIngress: + type: boolean + headService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + rayStartParams: + additionalProperties: + type: string + type: object + serviceType: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - rayStartParams + - template + type: object + headServiceAnnotations: + additionalProperties: + type: string + type: object + rayVersion: + type: string + suspend: + type: boolean + workerGroupSpecs: + items: + properties: + groupName: + type: string + maxReplicas: + default: 2147483647 + format: int32 + type: integer + minReplicas: + default: 0 + format: int32 + type: integer + rayStartParams: + additionalProperties: + type: string + type: object + replicas: + default: 0 + format: int32 + type: integer + scaleStrategy: + properties: + workersToDelete: + items: + type: string + type: array + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - groupName + - maxReplicas + - minReplicas + - rayStartParams + - template + type: object + type: array + required: + - headGroupSpec + type: object + runtimeEnvYAML: + type: string + shutdownAfterJobFinishes: + type: boolean + submitterPodTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + suspend: + type: boolean + ttlSecondsAfterFinished: + default: 0 + format: int32 + type: integer + required: + - entrypoint + type: object + status: + properties: + dashboardURL: + type: string + endTime: + format: date-time + type: string + jobDeploymentStatus: + type: string + jobId: + type: string + jobStatus: + type: string + message: + type: string + observedGeneration: + format: int64 + type: integer + rayClusterName: + type: string + rayClusterStatus: + properties: + availableWorkerReplicas: + format: int32 + type: integer + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + serviceIP: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + type: object + startTime: + format: date-time + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/deployment/helm/ray/kuberay-operator/crds/ray.io_rayservices.yaml b/deployment/helm/ray/kuberay-operator/crds/ray.io_rayservices.yaml new file mode 100644 index 0000000..57a4bc2 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/crds/ray.io_rayservices.yaml @@ -0,0 +1,16805 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: rayservices.ray.io +spec: + group: ray.io + names: + categories: + - all + kind: RayService + listKind: RayServiceList + plural: rayservices + singular: rayservice + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.serviceStatus + name: service status + type: string + - jsonPath: .status.numServeEndpoints + name: num serve endpoints + type: string + name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + deploymentUnhealthySecondThreshold: + format: int32 + type: integer + excludeHeadPodFromServeSvc: + type: boolean + rayClusterConfig: + properties: + autoscalerOptions: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + idleTimeoutSeconds: + format: int32 + type: integer + image: + type: string + imagePullPolicy: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + upscalingMode: + enum: + - Default + - Aggressive + - Conservative + type: string + version: + enum: + - v1 + - v2 + type: string + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + type: object + enableInTreeAutoscaling: + type: boolean + gcsFaultToleranceOptions: + properties: + externalStorageNamespace: + type: string + redisAddress: + type: string + redisPassword: + properties: + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + type: object + redisUsername: + properties: + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + type: object + required: + - redisAddress + type: object + headGroupSpec: + properties: + enableIngress: + type: boolean + headService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + rayStartParams: + additionalProperties: + type: string + type: object + serviceType: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - template + type: object + headServiceAnnotations: + additionalProperties: + type: string + type: object + managedBy: + type: string + x-kubernetes-validations: + - message: the managedBy field is immutable + rule: self == oldSelf + - message: the managedBy field value must be either 'ray.io/kuberay-operator' + or 'kueue.x-k8s.io/multikueue' + rule: self in ['ray.io/kuberay-operator', 'kueue.x-k8s.io/multikueue'] + rayVersion: + type: string + suspend: + type: boolean + workerGroupSpecs: + items: + properties: + groupName: + type: string + idleTimeoutSeconds: + format: int32 + type: integer + maxReplicas: + default: 2147483647 + format: int32 + type: integer + minReplicas: + default: 0 + format: int32 + type: integer + numOfHosts: + default: 1 + format: int32 + type: integer + rayStartParams: + additionalProperties: + type: string + type: object + replicas: + default: 0 + format: int32 + type: integer + scaleStrategy: + properties: + workersToDelete: + items: + type: string + type: array + type: object + suspend: + type: boolean + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - groupName + - maxReplicas + - minReplicas + - template + type: object + type: array + required: + - headGroupSpec + type: object + serveConfigV2: + type: string + serveService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + serviceUnhealthySecondThreshold: + format: int32 + type: integer + upgradeStrategy: + properties: + type: + type: string + type: object + required: + - rayClusterConfig + type: object + status: + properties: + activeServiceStatus: + properties: + applicationStatuses: + additionalProperties: + properties: + message: + type: string + serveDeploymentStatuses: + additionalProperties: + properties: + message: + type: string + status: + type: string + type: object + type: object + status: + type: string + type: object + type: object + rayClusterName: + type: string + rayClusterStatus: + properties: + availableWorkerReplicas: + format: int32 + type: integer + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + podName: + type: string + serviceIP: + type: string + serviceName: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + stateTransitionTimes: + additionalProperties: + format: date-time + type: string + type: object + type: object + type: object + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lastUpdateTime: + format: date-time + type: string + numServeEndpoints: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + pendingServiceStatus: + properties: + applicationStatuses: + additionalProperties: + properties: + message: + type: string + serveDeploymentStatuses: + additionalProperties: + properties: + message: + type: string + status: + type: string + type: object + type: object + status: + type: string + type: object + type: object + rayClusterName: + type: string + rayClusterStatus: + properties: + availableWorkerReplicas: + format: int32 + type: integer + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + podName: + type: string + serviceIP: + type: string + serviceName: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + stateTransitionTimes: + additionalProperties: + format: date-time + type: string + type: object + type: object + type: object + serviceStatus: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + deploymentUnhealthySecondThreshold: + format: int32 + type: integer + rayClusterConfig: + properties: + autoscalerOptions: + properties: + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + idleTimeoutSeconds: + format: int32 + type: integer + image: + type: string + imagePullPolicy: + type: string + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + upscalingMode: + enum: + - Default + - Aggressive + - Conservative + type: string + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + type: object + enableInTreeAutoscaling: + type: boolean + headGroupSpec: + properties: + enableIngress: + type: boolean + headService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + rayStartParams: + additionalProperties: + type: string + type: object + serviceType: + type: string + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - rayStartParams + - template + type: object + headServiceAnnotations: + additionalProperties: + type: string + type: object + rayVersion: + type: string + suspend: + type: boolean + workerGroupSpecs: + items: + properties: + groupName: + type: string + maxReplicas: + default: 2147483647 + format: int32 + type: integer + minReplicas: + default: 0 + format: int32 + type: integer + rayStartParams: + additionalProperties: + type: string + type: object + replicas: + default: 0 + format: int32 + type: integer + scaleStrategy: + properties: + workersToDelete: + items: + type: string + type: array + type: object + template: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - groupName + - maxReplicas + - minReplicas + - rayStartParams + - template + type: object + type: array + required: + - headGroupSpec + type: object + serveConfigV2: + type: string + serveService: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + internalTrafficPolicy: + type: string + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerClass: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + trafficDistribution: + type: string + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ipMode: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + type: string + required: + - error + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + type: object + serviceUnhealthySecondThreshold: + format: int32 + type: integer + type: object + status: + properties: + activeServiceStatus: + properties: + applicationStatuses: + additionalProperties: + properties: + healthLastUpdateTime: + format: date-time + type: string + message: + type: string + serveDeploymentStatuses: + additionalProperties: + properties: + healthLastUpdateTime: + format: date-time + type: string + message: + type: string + status: + type: string + type: object + type: object + status: + type: string + type: object + type: object + rayClusterName: + type: string + rayClusterStatus: + properties: + availableWorkerReplicas: + format: int32 + type: integer + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + serviceIP: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + type: object + type: object + lastUpdateTime: + format: date-time + type: string + observedGeneration: + format: int64 + type: integer + pendingServiceStatus: + properties: + applicationStatuses: + additionalProperties: + properties: + healthLastUpdateTime: + format: date-time + type: string + message: + type: string + serveDeploymentStatuses: + additionalProperties: + properties: + healthLastUpdateTime: + format: date-time + type: string + message: + type: string + status: + type: string + type: object + type: object + status: + type: string + type: object + type: object + rayClusterName: + type: string + rayClusterStatus: + properties: + availableWorkerReplicas: + format: int32 + type: integer + desiredCPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredGPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredMemory: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredTPU: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + desiredWorkerReplicas: + format: int32 + type: integer + endpoints: + additionalProperties: + type: string + type: object + head: + properties: + podIP: + type: string + serviceIP: + type: string + type: object + lastUpdateTime: + format: date-time + nullable: true + type: string + maxWorkerReplicas: + format: int32 + type: integer + minWorkerReplicas: + format: int32 + type: integer + observedGeneration: + format: int64 + type: integer + readyWorkerReplicas: + format: int32 + type: integer + reason: + type: string + state: + type: string + type: object + type: object + serviceStatus: + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} diff --git a/deployment/helm/ray/kuberay-operator/templates/_helpers.tpl b/deployment/helm/ray/kuberay-operator/templates/_helpers.tpl new file mode 100644 index 0000000..7490351 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/_helpers.tpl @@ -0,0 +1,322 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "kuberay-operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Allow the component label to be overridden, otherwise provide a default value. +*/}} +{{- define "kuberay-operator.component" -}} +{{- default .Chart.Name .Values.componentOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "kuberay-operator.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "kuberay-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "kuberay-operator.labels" -}} +app.kubernetes.io/name: {{ include "kuberay-operator.name" . }} +helm.sh/chart: {{ include "kuberay-operator.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{- /* Create the name of the deployment to use. */ -}} +{{- define "kuberay-operator.deployment.name" -}} +{{- include "kuberay-operator.fullname" . }} +{{- end -}} + +{{/* +FeatureGates +*/}} +{{- define "kuberay.featureGates" -}} +{{- $features := "" }} +{{- range .Values.featureGates }} + {{- $str := printf "%s=%t," .name .enabled }} + {{- $features = print $features $str }} +{{- end }} +{{- with .Values.featureGates }} +--feature-gates={{ $features | trimSuffix "," }} +{{- end }} +{{- end }} + +{{- /* Create the name of the service to use. */ -}} +{{- define "kuberay-operator.service.name" -}} +{{- include "kuberay-operator.fullname" . }} +{{- end -}} + +{{- /* Create the name of the service account to use. */ -}} +{{- define "kuberay-operator.serviceAccount.name" -}} +{{- if .Values.serviceAccount.create -}} +{{- default (include "kuberay-operator.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{- /* Create the name of the cluster role to use. */ -}} +{{- define "kuberay-operator.clusterRole.name" -}} +{{- include "kuberay-operator.fullname" . -}} +{{- end -}} + +{{- /* Create the name of the cluster role binding to use. */ -}} +{{- define "kuberay-operator.clusterRoleBinding.name" -}} +{{- include "kuberay-operator.fullname" . -}} +{{- end -}} + +{{- /* Create the name of the role to use. */ -}} +{{- define "kuberay-operator.role.name" -}} +{{- include "kuberay-operator.fullname" . -}} +{{- end -}} + +{{- /* Create the name of the role binding to use. */ -}} +{{- define "kuberay-operator.roleBinding.name" -}} +{{- include "kuberay-operator.fullname" . -}} +{{- end -}} + +{{- /* Create the name of the leader election role to use. */ -}} +{{- define "kuberay-operator.leaderElectionRole.name" -}} +{{- include "kuberay-operator.fullname" . -}}-leader-election +{{- end -}} + +{{- /* Create the name of the leader election role binding to use. */ -}} +{{- define "kuberay-operator.leaderElectionRoleBinding.name" -}} +{{- include "kuberay-operator.fullname" . -}}-leader-election +{{- end -}} + +{{/* +Create a template to ensure consistency for Role and ClusterRole. +*/}} +{{- define "role.consistentRules" -}} +rules: +- apiGroups: + - "" + resources: + - endpoints + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + - pods/status + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/proxy + - services/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - list + - watch +- apiGroups: + - "" + resources: + - services/proxy + verbs: + - create + - get + - patch + - update +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +- apiGroups: + - ray.io + resources: + - rayclusters + - rayjobs + - rayservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ray.io + resources: + - rayclusters/finalizers + - rayjobs/finalizers + - rayservices/finalizers + verbs: + - update +- apiGroups: + - ray.io + resources: + - rayclusters/status + - rayjobs/status + - rayservices/status + verbs: + - get + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + verbs: + - create + - delete + - get + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +{{- if or .batchSchedulerEnabled (eq .batchSchedulerName "volcano") }} +- apiGroups: + - scheduling.volcano.sh + resources: + - podgroups + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get +{{- end -}} +{{- if or .batchSchedulerEnabled (eq .batchSchedulerName "scheduler-plugins") }} +- apiGroups: + - scheduling.x-k8s.io + resources: + - podgroups + verbs: + - create + - get + - list + - watch +{{- end -}} +{{- end -}} diff --git a/deployment/helm/ray/kuberay-operator/templates/deployment.yaml b/deployment/helm/ray/kuberay-operator/templates/deployment.yaml new file mode 100644 index 0000000..e64d95c --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/deployment.yaml @@ -0,0 +1,150 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "kuberay-operator.deployment.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: {{ include "kuberay-operator.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "kuberay-operator.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ include "kuberay-operator.component" . }} + {{- with .Values.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "kuberay-operator.serviceAccount.name" . }} + {{- if and (.Values.logging.baseDir) (.Values.logging.fileName) }} + volumes: + - name: kuberay-logs + {{- if .Values.logging.sizeLimit }} + emptyDir: + sizeLimit: {{ .Values.logging.sizeLimit }} + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + {{- with .Values.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- if and (.Values.logging.baseDir) (.Values.logging.fileName) }} + volumeMounts: + - name: kuberay-logs + mountPath: "{{ .Values.logging.baseDir }}" + {{- end }} + command: + - {{ .Values.operatorCommand }} + args: + {{- $argList := list -}} + {{- $argList = append $argList (include "kuberay.featureGates" . | trim) -}} + {{- if .Values.batchScheduler -}} + {{- if .Values.batchScheduler.enabled -}} + {{- $argList = append $argList "--enable-batch-scheduler" -}} + {{- end -}} + {{- if .Values.batchScheduler.name -}} + {{- $argList = append $argList (printf "--batch-scheduler=%s" .Values.batchScheduler.name) -}} + {{- end -}} + {{- end -}} + {{- $watchNamespace := "" -}} + {{- if and .Values.singleNamespaceInstall (not .Values.watchNamespace) -}} + {{- $watchNamespace = .Release.Namespace -}} + {{- else if .Values.watchNamespace -}} + {{- $watchNamespace = join "," .Values.watchNamespace -}} + {{- end -}} + {{- if $watchNamespace -}} + {{- $argList = append $argList "--watch-namespace" -}} + {{- $argList = append $argList $watchNamespace -}} + {{- end -}} + {{- if and (.Values.logging.baseDir) (.Values.logging.fileName) -}} + {{- $argList = append $argList "--log-file-path" -}} + {{- $argList = append $argList (printf "%s/%s" .Values.logging.baseDir .Values.logging.fileName) -}} + {{- end -}} + {{- if .Values.logging.stdoutEncoder -}} + {{- $argList = append $argList "--log-stdout-encoder" -}} + {{- $argList = append $argList .Values.logging.stdoutEncoder -}} + {{- end -}} + {{- if .Values.logging.fileEncoder -}} + {{- $argList = append $argList "--log-file-encoder" -}} + {{- $argList = append $argList .Values.logging.fileEncoder -}} + {{- end -}} + {{- if hasKey .Values "useKubernetesProxy" -}} + {{- $argList = append $argList (printf "--use-kubernetes-proxy=%t" .Values.useKubernetesProxy) -}} + {{- end -}} + {{- if hasKey .Values "leaderElectionEnabled" -}} + {{- $argList = append $argList (printf "--enable-leader-election=%t" .Values.leaderElectionEnabled) -}} + {{- end -}} + {{- if and (hasKey .Values "metrics") (hasKey .Values.metrics "enabled") }} + {{- $argList = append $argList (printf "--enable-metrics=%t" .Values.metrics.enabled) -}} + {{- end -}} + {{- (printf "\n") -}} + {{- $argList | toYaml | indent 12 }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + {{- with .Values.env }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} + livenessProbe: + httpGet: + path: /metrics + port: http + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + readinessProbe: + httpGet: + path: /metrics + port: http + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/leader_election_role.yaml b/deployment/helm/ray/kuberay-operator/templates/leader_election_role.yaml new file mode 100644 index 0000000..774c10b --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/leader_election_role.yaml @@ -0,0 +1,37 @@ +{{- if .Values.rbacEnable }} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "kuberay-operator.leaderElectionRole.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/leader_election_role_binding.yaml b/deployment/helm/ray/kuberay-operator/templates/leader_election_role_binding.yaml new file mode 100644 index 0000000..e9b6e9e --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/leader_election_role_binding.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rbacEnable -}} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "kuberay-operator.leaderElectionRoleBinding.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "kuberay-operator.leaderElectionRole.name" . }} +subjects: +- kind: ServiceAccount + name: {{ include "kuberay-operator.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_role.yaml b/deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_role.yaml new file mode 100644 index 0000000..6849c7a --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_role.yaml @@ -0,0 +1,13 @@ +{{- if and .Values.rbacEnable .Values.singleNamespaceInstall .Values.crNamespacedRbacEnable }} +{{- $watchNamespaces := default (list .Release.Namespace) .Values.watchNamespace }} +{{- range $namespace := $watchNamespaces }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "kuberay-operator.fullname" $ }} + namespace: {{ $namespace }} + labels: {{ include "kuberay-operator.labels" $ | nindent 4 }} +{{ include "role.consistentRules" (dict "batchSchedulerEnabled" $.Values.batchScheduler.enabled) }} +{{- end }} +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_rolebinding.yaml b/deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_rolebinding.yaml new file mode 100644 index 0000000..1898363 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/multiple_namespaces_rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.rbacEnable .Values.singleNamespaceInstall .Values.crNamespacedRbacEnable }} +{{- $watchNamespaces := default (list .Release.Namespace) .Values.watchNamespace }} +{{- range $namespace := $watchNamespaces }} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "kuberay-operator.fullname" $ }} + namespace: {{ $namespace }} + labels: {{ include "kuberay-operator.labels" $ | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "kuberay-operator.fullname" $ }} +subjects: +- kind: ServiceAccount + name: {{ include "kuberay-operator.serviceAccount.name" $ }} + namespace: {{ $.Release.Namespace }} +{{- end }} +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/ray_rayjob_editor_role.yaml b/deployment/helm/ray/kuberay-operator/templates/ray_rayjob_editor_role.yaml new file mode 100644 index 0000000..7c0e4b4 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/ray_rayjob_editor_role.yaml @@ -0,0 +1,28 @@ +{{- /* ClusterRole for end users to view and edit RayJob. */ -}} +{{- if and .Values.rbacEnable (not .Values.singleNamespaceInstall) }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: rayjob-editor-role + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - ray.io + resources: + - rayjobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ray.io + resources: + - rayjobs/status + verbs: + - get +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/ray_rayjob_viewer_role.yaml b/deployment/helm/ray/kuberay-operator/templates/ray_rayjob_viewer_role.yaml new file mode 100644 index 0000000..3267b2a --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/ray_rayjob_viewer_role.yaml @@ -0,0 +1,24 @@ +{{- /* ClusterRole for end users to view RayJob. */ -}} +{{- if and .Values.rbacEnable (not .Values.singleNamespaceInstall) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: rayjob-viewer-role + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - ray.io + resources: + - rayjobs + verbs: + - get + - list + - watch +- apiGroups: + - ray.io + resources: + - rayjobs/status + verbs: + - get +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/ray_rayservice_editor_role.yaml b/deployment/helm/ray/kuberay-operator/templates/ray_rayservice_editor_role.yaml new file mode 100644 index 0000000..21578ab --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/ray_rayservice_editor_role.yaml @@ -0,0 +1,28 @@ +{{- /* ClusterRole for end users to view and edit RayService. */ -}} +{{- if and .Values.rbacEnable (not .Values.singleNamespaceInstall) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: rayservice-editor-role + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - ray.io + resources: + - rayservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ray.io + resources: + - rayservices/status + verbs: + - get +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/ray_rayservice_viewer_role.yaml b/deployment/helm/ray/kuberay-operator/templates/ray_rayservice_viewer_role.yaml new file mode 100644 index 0000000..f8af425 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/ray_rayservice_viewer_role.yaml @@ -0,0 +1,24 @@ +{{- /* ClusterRole for end users to view RayService. */ -}} +{{- if and .Values.rbacEnable (not .Values.singleNamespaceInstall) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: rayservice-viewer-role + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - ray.io + resources: + - rayservices + verbs: + - get + - list + - watch +- apiGroups: + - ray.io + resources: + - rayservices/status + verbs: + - get +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/role.yaml b/deployment/helm/ray/kuberay-operator/templates/role.yaml new file mode 100644 index 0000000..6ce4fa6 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/role.yaml @@ -0,0 +1,9 @@ +{{- if and .Values.rbacEnable (not .Values.singleNamespaceInstall) }} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "kuberay-operator.clusterRole.name" . }} + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +{{ include "role.consistentRules" (dict "batchSchedulerEnabled" .Values.batchScheduler.enabled "batchSchedulerName" .Values.batchScheduler.name) }} +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/rolebinding.yaml b/deployment/helm/ray/kuberay-operator/templates/rolebinding.yaml new file mode 100644 index 0000000..f9dd4cd --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/rolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.rbacEnable (not .Values.singleNamespaceInstall) }} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "kuberay-operator.clusterRoleBinding.name" . }} + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + name: {{ include "kuberay-operator.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "kuberay-operator.clusterRole.name" . }} +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/templates/service.yaml b/deployment/helm/ray/kuberay-operator/templates/service.yaml new file mode 100644 index 0000000..725f31d --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "kuberay-operator.service.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: {{ include "kuberay-operator.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/deployment/helm/ray/kuberay-operator/templates/serviceaccount.yaml b/deployment/helm/ray/kuberay-operator/templates/serviceaccount.yaml new file mode 100644 index 0000000..198bee5 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/serviceaccount.yaml @@ -0,0 +1,9 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "kuberay-operator.serviceAccount.name" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "kuberay-operator.labels" . | nindent 4 }} +{{- end -}} diff --git a/deployment/helm/ray/kuberay-operator/templates/servicemonitor.yaml b/deployment/helm/ray/kuberay-operator/templates/servicemonitor.yaml new file mode 100644 index 0000000..ad141e7 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/templates/servicemonitor.yaml @@ -0,0 +1,23 @@ +{{- if .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "kuberay-operator.fullname" . }} + namespace: {{ .Values.metrics.serviceMonitor.namespace | default .Release.Namespace }} + labels: + {{- with .Values.metrics.serviceMonitor.selector }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + endpoints: + - path: /metrics + targetPort: http + interval: {{ .Values.metrics.serviceMonitor.interval }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "kuberay-operator.name" . }} +{{- end }} diff --git a/deployment/helm/ray/kuberay-operator/values.yaml b/deployment/helm/ray/kuberay-operator/values.yaml new file mode 100644 index 0000000..8b0f382 --- /dev/null +++ b/deployment/helm/ray/kuberay-operator/values.yaml @@ -0,0 +1,221 @@ +# Default values for kuberay-operator. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- String to partially override release name. +nameOverride: kuberay-operator + +# -- String to fully override release name. +fullnameOverride: kuberay-operator + +# -- String to override component name. +componentOverride: kuberay-operator + +image: + # -- Image repository. + repository: quay.io/kuberay/operator + + # -- Image tag. + tag: v1.4.2 + + # -- Image pull policy. + pullPolicy: IfNotPresent + +# -- Extra labels. +labels: {} + +# -- Extra annotations. +annotations: {} + +serviceAccount: + # -- Specifies whether a service account should be created. + create: true + # -- The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template. + name: kuberay-operator + +logging: + # -- Log encoder to use for stdout (one of `json` or `console`). + stdoutEncoder: json + # -- Log encoder to use for file logging (one of `json` or `console`). + fileEncoder: json + # -- Directory for kuberay-operator log file. + baseDir: "" + # -- File name for kuberay-operator log file. + fileName: "" + # -- EmptyDir volume size limit for kuberay-operator log file. + sizeLimit: "" + +# Enable customized Kubernetes scheduler integration. If enabled, Ray workloads will be scheduled +# by the customized scheduler. +# * "enabled" is the legacy option and will be deprecated soon. +# * "name" is the standard option, expecting a scheduler name, supported values are +# "default", "volcano", "yunikorn", and "scheduler-plugins". +# +# Note: "enabled" and "name" should not be set at the same time. If both are set, an error will be thrown. +# +# Examples: +# 1. Use volcano (deprecated) +# batchScheduler: +# enabled: true +# +# 2. Use volcano +# batchScheduler: +# name: volcano +# +# 3. Use yunikorn +# batchScheduler: +# name: yunikorn +# +# 4. Use PodGroup +# batchScheduler: +# name: scheduler-plugins +# +batchScheduler: + # Deprecated. This option will be removed in the future. + # Note, for backwards compatibility. When it sets to true, it enables volcano scheduler integration. + enabled: false + # Set the customized scheduler name, supported values are "volcano" or "yunikorn", do not set + # "batchScheduler.enabled=true" at the same time as it will override this option. + name: "" + +featureGates: +- name: RayClusterStatusConditions + enabled: true +- name: RayJobDeletionPolicy + enabled: false + +# Configurations for KubeRay operator metrics. +metrics: + # -- Whether KubeRay operator should emit control plane metrics. + enabled: true + serviceMonitor: + # -- Enable a prometheus ServiceMonitor + enabled: false + # -- Prometheus ServiceMonitor interval + interval: 30s + # -- When true, honorLabels preserves the metric’s labels when they collide with the target’s labels. + honorLabels: true + # -- Prometheus ServiceMonitor selector + selector: {} + # release: prometheus + # -- Prometheus ServiceMonitor namespace + namespace: "" # "monitoring" + +# -- Path to the operator binary +operatorCommand: /manager + +# if userKubernetesProxy is set to true, the KubeRay operator will be configured with the --use-kubernetes-proxy flag. +# Using this option to configure kuberay-operator to comunitcate to Ray head pods by proxying through the Kubernetes API Server. +# useKubernetesProxy: true + +# -- If leaderElectionEnabled is set to true, the KubeRay operator will use leader election for high availability. +leaderElectionEnabled: true + +# -- If rbacEnable is set to false, no RBAC resources will be created, including the Role for leader election, the Role for Pods and Services, and so on. +rbacEnable: true + +# -- When crNamespacedRbacEnable is set to true, the KubeRay operator will create a Role for RayCluster preparation (e.g., Pods, Services) +# and a corresponding RoleBinding for each namespace listed in the "watchNamespace" parameter. Please note that even if crNamespacedRbacEnable +# is set to false, the Role and RoleBinding for leader election will still be created. +# +# Note: +# (1) This variable is only effective when rbacEnable and singleNamespaceInstall are both set to true. +# (2) In most cases, it should be set to true, unless you are using a Kubernetes cluster managed by GitOps tools such as ArgoCD. +crNamespacedRbacEnable: true + +# -- When singleNamespaceInstall is true: +# - Install namespaced RBAC resources such as Role and RoleBinding instead of cluster-scoped ones like ClusterRole and ClusterRoleBinding so that +# the chart can be installed by users with permissions restricted to a single namespace. +# (Please note that this excludes the CRDs, which can only be installed at the cluster scope.) +# - If "watchNamespace" is not set, the KubeRay operator will, by default, only listen +# to resource events within its own namespace. +singleNamespaceInstall: true + +# The KubeRay operator will watch the custom resources in the namespaces listed in the "watchNamespace" parameter. +# watchNamespace: +# - n1 +# - n2 + +# -- Environment variables. + +env: +# If not set or set to true, kuberay auto injects an init container waiting for ray GCS. +# If false, you will need to inject your own init container to ensure ray GCS is up before the ray workers start. +# Warning: we highly recommend setting to true and let kuberay handle for you. +# - name: ENABLE_INIT_CONTAINER_INJECTION +# value: "true" +# If set to true, kuberay creates a normal ClusterIP service for a Ray Head instead of a Headless service. Default to false. +- name: ENABLE_RAY_HEAD_CLUSTER_IP_SERVICE + value: "true" +# If not set or set to "", kuberay will pick up the default k8s cluster domain `cluster.local` +# Otherwise, kuberay will use your custom domain +# - name: CLUSTER_DOMAIN +# value: "" +# If not set or set to false, when running on OpenShift with Ingress creation enabled, kuberay will create OpenShift route +# Otherwise, regardless of the type of cluster with Ingress creation enabled, kuberay will create Ingress +# - name: USE_INGRESS_ON_OPENSHIFT +# value: "true" +# Unconditionally requeue after the number of seconds specified in the +# environment variable RAYCLUSTER_DEFAULT_REQUEUE_SECONDS_ENV. If the +# environment variable is not set, requeue after the default value (300). +# - name: RAYCLUSTER_DEFAULT_REQUEUE_SECONDS_ENV +# value: 300 +# If not set or set to "true", KubeRay will clean up the Redis storage namespace when a GCS FT-enabled RayCluster is deleted. +# - name: ENABLE_GCS_FT_REDIS_CLEANUP +# value: "true" +# For LLM serving, some users might not have sufficient GPU resources to run two RayClusters simultaneously. +# Therefore, KubeRay offers ENABLE_ZERO_DOWNTIME as a feature flag for zero-downtime upgrades. +# - name: ENABLE_ZERO_DOWNTIME +# value: "true" +# This environment variable for the KubeRay operator is used to determine whether to enable +# the injection of readiness and liveness probes into Ray head and worker containers. +# Enabling this feature contributes to the robustness of Ray clusters. +# - name: ENABLE_PROBES_INJECTION +# value: "true" +# If set to true, the RayJob CR itself will be deleted if shutdownAfterJobFinishes is set to true. Note that all resources created by the RayJob CR will be deleted, including the K8s Job. Otherwise, only the RayCluster CR will be deleted. Default is false. +# - name: DELETE_RAYJOB_CR_AFTER_JOB_FINISHES +# value: "false" + +# -- Resource requests and limits for containers. +resources: + limits: + cpu: 100m + # Anecdotally, managing 500 Ray pods requires roughly 500MB memory. + # Monitor memory usage and adjust as needed. + memory: 512Mi + # requests: + # cpu: 100m + # memory: 512Mi + +# @Ignore -- Pod liveness probe configuration. +livenessProbe: + initialDelaySeconds: 10 + periodSeconds: 5 + failureThreshold: 5 + +# @Ignore -- Pod readiness probe configuration. +readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 5 + failureThreshold: 5 + +# -- Set up `securityContext` to improve Pod security. +podSecurityContext: {} + +# @ignore -- Set up `securityContext` to improve container security. +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +service: + # -- Service type. + type: ClusterIP + # -- Service port. + port: 8080 diff --git a/deployment/helm/ray/ray-cluster/Chart.yaml b/deployment/helm/ray/ray-cluster/Chart.yaml new file mode 100644 index 0000000..14dd186 --- /dev/null +++ b/deployment/helm/ray/ray-cluster/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: ray-cluster +version: 1.4.2 +icon: https://github.com/ray-project/ray/raw/master/doc/source/images/ray_header_logo.png diff --git a/deployment/helm/ray/ray-cluster/templates/_helpers.tpl b/deployment/helm/ray/ray-cluster/templates/_helpers.tpl new file mode 100644 index 0000000..50da5d1 --- /dev/null +++ b/deployment/helm/ray/ray-cluster/templates/_helpers.tpl @@ -0,0 +1,55 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "ray-cluster.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "ray-cluster.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "ray-cluster.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "ray-cluster.labels" -}} +helm.sh/chart: {{ include "ray-cluster.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "ray-cluster.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "ray-cluster.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/deployment/helm/ray/ray-cluster/templates/raycluster-cluster.yaml b/deployment/helm/ray/ray-cluster/templates/raycluster-cluster.yaml new file mode 100644 index 0000000..5e17cf1 --- /dev/null +++ b/deployment/helm/ray/ray-cluster/templates/raycluster-cluster.yaml @@ -0,0 +1,407 @@ +apiVersion: ray.io/v1 +kind: RayCluster +metadata: + name: {{ include "ray-cluster.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "ray-cluster.labels" . | nindent 4 }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.head.rayVersion }} + rayVersion: {{ . }} + {{- end }} + {{- with .Values.head.enableInTreeAutoscaling }} + enableInTreeAutoscaling: {{ . }} + {{- end }} + {{- with .Values.head.autoscalerOptions }} + autoscalerOptions: + {{- toYaml . | nindent 4 }} + {{- end }} + headGroupSpec: + {{- with .Values.head.headService }} + headService: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.service.type }} + serviceType: {{ . }} + {{- end }} + {{- if or .Values.head.rayStartParams .Values.head.initArgs }} + rayStartParams: + {{- range $key, $val := .Values.head.rayStartParams }} + {{ $key }}: {{ $val | quote }} + {{- end }} + {{- /* initArgs is a deprecated alias for rayStartParams. */}} + {{- range $key, $val := .Values.head.initArgs }} + {{ $key }}: {{ $val | quote }} + {{- end }} + {{- else }} + rayStartParams: {} + {{- end }} + template: + metadata: + labels: + {{- include "ray-cluster.labels" . | nindent 10 }} + {{- with .Values.head.labels }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.annotations }} + annotations: + {{- toYaml . | nindent 10 }} + {{- end }} + spec: + {{- with .Values.head.initContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: ray-head + {{- if .Values.head.image }} + image: {{ .Values.head.image.repository }}:{{ .Values.head.image.tag }} + imagePullPolicy: {{ .Values.head.image.pullPolicy }} + {{- else }} + image: {{ .Values.image.repository }}:{{ .Values.image.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- end }} + {{- with .Values.head.command }} + command: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.args }} + args: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with concat .Values.common.containerEnv .Values.head.containerEnv }} + env: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.envFrom }} + envFrom: + {{- toYaml . | nindent 10 }} + {{- end }} + {{ with .Values.head.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.ports }} + ports: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.head.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.head.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.head.sidecarContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.volumes }} + volumes: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.dnsConfig }} + dnsConfig: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.affinity }} + affinity: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.tolerations }} + tolerations: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.head.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + {{- with .Values.head.priority }} + priority: {{ . }} + {{- end }} + {{- with .Values.head.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.head.restartPolicy }} + restartPolicy: {{ . }} + {{- end }} + {{- with .Values.head.serviceAccountName }} + serviceAccountName: {{ . }} + {{- end }} + {{- with .Values.head.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + workerGroupSpecs: + {{- if not .Values.worker.disabled }} + - groupName: {{ .Values.worker.groupName }} + replicas: {{ .Values.worker.replicas }} + minReplicas: {{ .Values.worker.minReplicas | default 0 }} + maxReplicas: {{ .Values.worker.maxReplicas | default 2147483647 }} + numOfHosts: {{ .Values.worker.numOfHosts | default 1 }} + {{- if or .Values.worker.rayStartParams .Values.worker.initArgs }} + rayStartParams: + {{- range $key, $val := .Values.worker.rayStartParams }} + {{ $key }}: {{ $val | quote }} + {{- end }} + {{- /* initArgs is a deprecated alias for rayStartParams. */}} + {{- range $key, $val := .Values.worker.initArgs }} + {{ $key }}: {{ $val | quote }} + {{- end }} + {{- else }} + rayStartParams: {} + {{- end }} + template: + metadata: + labels: + {{- include "ray-cluster.labels" . | nindent 10 }} + {{- with .Values.worker.labels }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.annotations }} + annotations: + {{- toYaml . | nindent 10 }} + {{- end }} + spec: + {{- with .Values.worker.initContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: ray-worker + {{- if .Values.worker.image }} + image: {{ .Values.worker.image.repository }}:{{ .Values.worker.image.tag }} + imagePullPolicy: {{ .Values.worker.image.pullPolicy }} + {{- else }} + image: {{ .Values.image.repository }}:{{ .Values.image.tag }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- end }} + {{- with .Values.worker.command }} + command: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.args }} + args: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with concat .Values.common.containerEnv .Values.worker.containerEnv }} + env: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.envFrom }} + envFrom: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.ports }} + ports: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.worker.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.worker.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.worker.sidecarContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.worker.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.worker.dnsConfig }} + dnsConfig: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.affinity }} + affinity: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.worker.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.worker.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + {{- with .Values.worker.priority }} + priority: {{ . }} + {{- end }} + {{- with .Values.worker.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.worker.restartPolicy }} + restartPolicy: {{ . }} + {{- end }} + {{- with .Values.worker.serviceAccountName }} + serviceAccountName: {{ . }} + {{- end }} + {{- with .Values.worker.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- range $groupName, $values := .Values.additionalWorkerGroups }} + {{- if not $values.disabled }} + - groupName: {{ $groupName }} + replicas: {{ $values.replicas }} + minReplicas: {{ $values.minReplicas | default 0 }} + maxReplicas: {{ $values.maxReplicas | default 2147483647 }} + numOfHosts: {{ $values.numOfHosts | default 1 }} + {{- if or $values.rayStartParams $values.initArgs }} + rayStartParams: + {{- range $key, $val := $values.rayStartParams }} + {{ $key }}: {{ $val | quote }} + {{- end }} + {{- /* initArgs is a deprecated alias for rayStartParams. */}} + {{- range $key, $val := $values.initArgs }} + {{ $key }}: {{ $val | quote }} + {{- end }} + {{- else }} + rayStartParams: {} + {{- end }} + template: + metadata: + labels: + {{- include "ray-cluster.labels" $ | nindent 10 }} + {{- with $values.labels }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.annotations }} + annotations: + {{- toYaml . | nindent 10 }} + {{- end }} + spec: + {{- with $values.initContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: ray-worker + {{- if $values.image }} + image: {{ $values.image.repository }}:{{ $values.image.tag }} + imagePullPolicy: {{ $values.image.pullPolicy }} + {{- else }} + image: {{ $.Values.image.repository }}:{{ $.Values.image.tag }} + imagePullPolicy: {{ $.Values.image.pullPolicy }} + {{- end }} + {{- with $values.command }} + command: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.args }} + args: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with concat $.Values.common.containerEnv ($values.containerEnv | default list) }} + env: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.envFrom }} + envFrom: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.ports }} + ports: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $values.sidecarContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $.Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.dnsConfig }} + dnsConfig: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.affinity }} + affinity: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with $values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + {{- with $values.priority }} + priority: {{ . }} + {{- end }} + {{- with $values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $values.restartPolicy }} + restartPolicy: {{ . }} + {{- end }} + {{- with $values.serviceAccountName }} + serviceAccountName: {{ . }} + {{- end }} + {{- with $values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} diff --git a/deployment/helm/ray/ray-cluster/values.yaml b/deployment/helm/ray/ray-cluster/values.yaml new file mode 100644 index 0000000..ef08f03 --- /dev/null +++ b/deployment/helm/ray/ray-cluster/values.yaml @@ -0,0 +1,396 @@ +# Default values for ray-cluster. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# The KubeRay community welcomes PRs to expose additional configuration +# in this Helm chart. + +image: + repository: runtime + tag: latest + pullPolicy: IfNotPresent + +nameOverride: "kuberay" +fullnameOverride: "" + +imagePullSecrets: [] +# - name: an-existing-secret + +# common defined values shared between the head and worker +common: + # containerEnv specifies environment variables for the Ray head and worker containers. + # Follows standard K8s container env schema. + containerEnv: [] + # - name: BLAH + # value: VAL +head: + # rayVersion determines the autoscaler's image version. + # It should match the Ray version in the image of the containers. + # rayVersion: 2.46.0 + # If enableInTreeAutoscaling is true, the autoscaler sidecar will be added to the Ray head pod. + # Ray autoscaler integration is supported only for Ray versions >= 1.11.0 + # Ray autoscaler integration is Beta with KubeRay >= 0.3.0 and Ray >= 2.0.0. + # enableInTreeAutoscaling: true + # autoscalerOptions is an OPTIONAL field specifying configuration overrides for the Ray autoscaler. + # The example configuration shown below represents the DEFAULT values. + # autoscalerOptions: + # upscalingMode: Default + # idleTimeoutSeconds is the number of seconds to wait before scaling down a worker pod which is not using Ray resources. + # idleTimeoutSeconds: 60 + # imagePullPolicy optionally overrides the autoscaler container's default image pull policy (IfNotPresent). + # imagePullPolicy: IfNotPresent + # Optionally specify the autoscaler container's securityContext. + # securityContext: {} + # env: [] + # envFrom: [] + # resources specifies optional resource request and limit overrides for the autoscaler container. + # For large Ray clusters, we recommend monitoring container resource usage to determine if overriding the defaults is required. + # resources: + # limits: + # cpu: "500m" + # memory: "512Mi" + # requests: + # cpu: "500m" + # memory: "512Mi" + initContainers: [] + labels: {} + # Note: From KubeRay v0.6.0, users need to create the ServiceAccount by themselves if they specify the `serviceAccountName` + # in the headGroupSpec. See https://github.com/ray-project/kuberay/pull/1128 for more details. + serviceAccountName: "" + restartPolicy: "" + rayStartParams: + object-store-memory: '78643200' + # containerEnv specifies environment variables for the Ray container, + # Follows standard K8s container env schema. + containerEnv: + - name: RAY_DEDUP_LOGS + value: "0" + - name: RAY_TQDM_PATCH_PRINT + value: "0" + - name: MYSQL_HOST + value: "mysql" + - name: MYSQL_PORT + value: "3306" + - name: MYSQL_USER + value: "root" + - name: MYSQL_PASSWORD + value: "Huawei@123" + - name: MYSQL_DATABASE + value: "datamate" + # - name: EXAMPLE_ENV + # value: "1" + envFrom: [] + # - secretRef: + # name: my-env-secret + # ports optionally allows specifying ports for the Ray container. + # ports: [] + # resource requests and limits for the Ray head container. + # Modify as needed for your application. + # Note that the resources in this example are much too small for production; + # we don't recommend allocating less than 8G memory for a Ray pod in production. + # Ray pods should be sized to take up entire K8s nodes when possible. + # Always set CPU and memory limits for Ray pods. + # It is usually best to set requests equal to limits. + # See https://docs.ray.io/en/latest/cluster/kubernetes/user-guides/config.html#resources + # for further guidance. + resources: + limits: + cpu: "2" + # To avoid out-of-memory issues, never allocate less than 2G memory for the Ray head. + memory: "4G" + requests: + cpu: "1" + memory: "2G" + annotations: {} + nodeSelector: {} + tolerations: [] + affinity: {} + # Pod security context. + podSecurityContext: {} + # Ray container security context. + securityContext: {} + # Optional: The following volumes/volumeMounts configurations are optional but recommended because + # Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr. + volumes: + - name: log-volume + hostPath: + path: /opt/data-mate/data/log + type: DirectoryOrCreate + - name: dataset-volume + hostPath: + path: /opt/data-mate/data/dataset + type: DirectoryOrCreate + - name: flow-volume + hostPath: + path: /opt/data-mate/data/flow + type: DirectoryOrCreate + volumeMounts: + - mountPath: /tmp/ray + name: log-volume + subPath: ray/head + - mountPath: /dataset + name: dataset-volume + - mountPath: /flow + name: flow-volume + # sidecarContainers specifies additional containers to attach to the Ray pod. + # Follows standard K8s container spec. + sidecarContainers: + - name: runtime + image: runtime + imagePullPolicy: IfNotPresent + command: + - python + - /opt/runtime/datamate/operator_runtime.py + - --port + - "8081" + env: + - name: MYSQL_HOST + value: "mysql" + - name: MYSQL_PORT + value: "3306" + - name: MYSQL_USER + value: "root" + - name: MYSQL_PASSWORD + value: "Huawei@123" + - name: MYSQL_DATABASE + value: "datamate" + ports: + - containerPort: 8081 + volumeMounts: + - mountPath: /tmp/ray + name: log-volume + subPath: ray/head + - mountPath: /var/log/data-mate + name: log-volume + - mountPath: /dataset + name: dataset-volume + - mountPath: /flow + name: flow-volume + # See docs/guidance/pod-command.md for more details about how to specify + # container command for head Pod. + command: [] + args: [] + # Optional, for the user to provide any additional fields to the service. + # See https://pkg.go.dev/k8s.io/Kubernetes/pkg/api/v1#Service + headService: {} + # metadata: + # annotations: + # prometheus.io/scrape: "true" + + # Custom pod DNS configuration + # See https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config + # dnsConfig: + # nameservers: + # - 8.8.8.8 + # searches: + # - example.local + # options: + # - name: ndots + # value: "2" + # - name: edns0 + topologySpreadConstraints: [] + + +worker: + # If you want to disable the default workergroup + # uncomment the line below + # disabled: true + groupName: workergroup + replicas: 1 + minReplicas: 1 + maxReplicas: 3 + labels: {} + serviceAccountName: "" + restartPolicy: "" + rayStartParams: {} + initContainers: [] + # containerEnv specifies environment variables for the Ray container, + # Follows standard K8s container env schema. + containerEnv: + - name: RAY_DEDUP_LOGS + value: "0" + - name: RAY_TQDM_PATCH_PRINT + value: "0" + - name: MYSQL_HOST + value: "mysql" + - name: MYSQL_PORT + value: "3306" + - name: MYSQL_USER + value: "root" + - name: MYSQL_PASSWORD + value: "Huawei@123" + - name: MYSQL_DATABASE + value: "datamate" + # - name: EXAMPLE_ENV + # value: "1" + envFrom: [] + # - secretRef: + # name: my-env-secret + # ports optionally allows specifying ports for the Ray container. + # ports: [] + # resource requests and limits for the Ray head container. + # Modify as needed for your application. + # Note that the resources in this example are much too small for production; + # we don't recommend allocating less than 8G memory for a Ray pod in production. + # Ray pods should be sized to take up entire K8s nodes when possible. + # Always set CPU and memory limits for Ray pods. + # It is usually best to set requests equal to limits. + # See https://docs.ray.io/en/latest/cluster/kubernetes/user-guides/config.html#resources + # for further guidance. + resources: + limits: + cpu: "4" + memory: "8G" + requests: + cpu: "1" + memory: "1G" + annotations: {} + nodeSelector: {} + tolerations: [] + affinity: {} + # Pod security context. + podSecurityContext: {} + # Ray container security context. + securityContext: {} + # Optional: The following volumes/volumeMounts configurations are optional but recommended because + # Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr. + volumes: + - name: log-volume + hostPath: + path: /opt/data-mate/data/log + type: DirectoryOrCreate + - name: dataset-volume + hostPath: + path: /opt/data-mate/data/dataset + type: DirectoryOrCreate + - name: flow-volume + hostPath: + path: /opt/data-mate/data/flow + type: DirectoryOrCreate + volumeMounts: + - mountPath: /tmp/ray + name: log-volume + subPath: ray/worker + - mountPath: /dataset + name: dataset-volume + - mountPath: /flow + name: flow-volume + # sidecarContainers specifies additional containers to attach to the Ray pod. + # Follows standard K8s container spec. + sidecarContainers: [] + # See docs/guidance/pod-command.md for more details about how to specify + # container command for worker Pod. + command: [] + args: [] + topologySpreadConstraints: [] + + + # Custom pod DNS configuration + # See https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config + # dnsConfig: + # nameservers: + # - 8.8.8.8 + # searches: + # - example.local + # options: + # - name: ndots + # value: "2" + # - name: edns0 + +# The map's key is used as the groupName. +# For example, key:small-group in the map below +# will be used as the groupName +additionalWorkerGroups: + smallGroup: + # Disabled by default + disabled: true + replicas: 0 + minReplicas: 0 + maxReplicas: 3 + labels: {} + serviceAccountName: "" + restartPolicy: "" + rayStartParams: {} + # containerEnv specifies environment variables for the Ray container, + # Follows standard K8s container env schema. + containerEnv: [] + # - name: EXAMPLE_ENV + # value: "1" + envFrom: [] + # - secretRef: + # name: my-env-secret + # ports optionally allows specifying ports for the Ray container. + # ports: [] + # resource requests and limits for the Ray head container. + # Modify as needed for your application. + # Note that the resources in this example are much too small for production; + # we don't recommend allocating less than 8G memory for a Ray pod in production. + # Ray pods should be sized to take up entire K8s nodes when possible. + # Always set CPU and memory limits for Ray pods. + # It is usually best to set requests equal to limits. + # See https://docs.ray.io/en/latest/cluster/kubernetes/user-guides/config.html#resources + # for further guidance. + resources: + limits: + cpu: 1 + memory: "1G" + requests: + cpu: 1 + memory: "1G" + annotations: {} + nodeSelector: {} + tolerations: [] + affinity: {} + # Pod security context. + podSecurityContext: {} + # Ray container security context. + securityContext: {} + # Optional: The following volumes/volumeMounts configurations are optional but recommended because + # Ray writes logs to /tmp/ray/session_latests/logs instead of stdout/stderr. + volumes: + - name: log-volume + hostPath: + path: /opt/data-mate/data/log + type: DirectoryOrCreate + - name: dataset-volume + hostPath: + path: /opt/data-mate/data/dataset + type: DirectoryOrCreate + - name: flow-volume + hostPath: + path: /opt/data-mate/data/flow + type: DirectoryOrCreate + volumeMounts: + - mountPath: /tmp/ray + name: log-volume + subPath: ray + - mountPath: /dataset + name: dataset-volume + - mountPath: /flow + name: flow-volume + sidecarContainers: [] + # See docs/guidance/pod-command.md for more details about how to specify + # container command for worker Pod. + command: [] + args: [] + + # Topology Spread Constraints for worker pods + # See: https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ + topologySpreadConstraints: [] + + # Custom pod DNS configuration + # See https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config + # dnsConfig: + # nameservers: + # - 8.8.8.8 + # searches: + # - example.local + # options: + # - name: ndots + # value: "2" + # - name: edns0 + +# Configuration for Head's Kubernetes Service +service: + # This is optional, and the default is ClusterIP. + type: NodePort diff --git a/deployment/helm/ray/service.yaml b/deployment/helm/ray/service.yaml new file mode 100644 index 0000000..c0c69c2 --- /dev/null +++ b/deployment/helm/ray/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: runtime + labels: + ray.io/node-type: head +spec: + type: ClusterIP + ports: + - port: 8081 + targetPort: 8081 + protocol: TCP + selector: + ray.io/node-type: head + diff --git a/deployment/kubernetes/backend/deploy.yaml b/deployment/kubernetes/backend/deploy.yaml new file mode 100644 index 0000000..4c4c95f --- /dev/null +++ b/deployment/kubernetes/backend/deploy.yaml @@ -0,0 +1,120 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app: backend + name: backend +rules: + - verbs: + - create + - list + - get + - delete + apiGroups: + - batch + resources: + - jobs + - verbs: + - list + apiGroups: + - "" + resources: + - pods + - verbs: + - get + - list + apiGroups: + - "" + resources: + - pods/log + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: backend + name: backend + +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + labels: + app: backend + name: backend +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: backend +subjects: + - kind: ServiceAccount + name: backend + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend + labels: + app: backend +spec: + replicas: 1 + selector: + matchLabels: + app: backend + template: + metadata: + labels: + app: backend + spec: + serviceAccountName: backend + containers: + - name: backend + image: backend + imagePullPolicy: IfNotPresent + env: + - name: namespace + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SPRING_CONFIG_LOCATION + value: file:/opt/backend/application.yml + ports: + - containerPort: 8080 + volumeMounts: + - name: dataset-volume + mountPath: /dataset + - name: flow-volume + mountPath: /flow + - name: log-volume + mountPath: /var/log/data-mate + volumes: + - name: dataset-volume + hostPath: + path: /opt/data-mate/data/dataset + type: DirectoryOrCreate + - name: flow-volume + hostPath: + path: /opt/data-mate/data/flow + type: DirectoryOrCreate + - name: log-volume + hostPath: + path: /opt/data-mate/data/log + type: DirectoryOrCreate + +--- +apiVersion: v1 +kind: Service +metadata: + name: backend + labels: + app: backend +spec: + type: ClusterIP + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + selector: + app: backend diff --git a/deployment/kubernetes/datax/deploy.yaml b/deployment/kubernetes/datax/deploy.yaml new file mode 100644 index 0000000..ba8f0af --- /dev/null +++ b/deployment/kubernetes/datax/deploy.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: datax + labels: + app: datax +spec: + replicas: 1 + selector: + matchLabels: + app: datax + template: + metadata: + labels: + app: datax + spec: + containers: + - name: datax + image: datax + imagePullPolicy: IfNotPresent + securityContext: + capabilities: + add: + - SYS_ADMIN + command: + - bash + - -c + - rpcbind && python3 /opt/datax/bin/app.py + ports: + - containerPort: 8000 + volumeMounts: + - name: dataset + mountPath: /dataset + subPath: dataset + volumes: + - name: dataset + hostPath: + path: /tmp/data-mate + +--- +apiVersion: v1 +kind: Service +metadata: + name: datax + labels: + app: datax +spec: + type: ClusterIP + ports: + - port: 8000 + targetPort: 8000 + protocol: TCP + selector: + app: datax diff --git a/deployment/kubernetes/frontend/deploy.yaml b/deployment/kubernetes/frontend/deploy.yaml new file mode 100644 index 0000000..5c56f2c --- /dev/null +++ b/deployment/kubernetes/frontend/deploy.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend + labels: + app: frontend +spec: + replicas: 1 + selector: + matchLabels: + app: frontend + template: + metadata: + labels: + app: frontend + spec: + containers: + - name: frontend + image: frontend + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend + labels: + app: frontend +spec: + type: NodePort + ports: + - port: 80 + targetPort: 80 + nodePort: 30000 + protocol: TCP + selector: + app: frontend diff --git a/deployment/kubernetes/mineru/deploy.yaml b/deployment/kubernetes/mineru/deploy.yaml new file mode 100644 index 0000000..ea0e678 --- /dev/null +++ b/deployment/kubernetes/mineru/deploy.yaml @@ -0,0 +1,52 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mineru + labels: + app: mineru +spec: + replicas: 1 + selector: + matchLabels: + app: mineru + template: + metadata: + labels: + app: mineru + spec: + containers: + - name: mineru + image: mineru + imagePullPolicy: IfNotPresent + command: + - mineru-api + args: + - --host + - "0.0.0.0" + - --port + - "8000" + ports: + - containerPort: 8000 + volumeMounts: + - name: tmp + mountPath: /tmp/data-mate + volumes: + - name: tmp + hostPath: + path: /tmp/data-mate + +--- +apiVersion: v1 +kind: Service +metadata: + name: mineru + labels: + app: mineru +spec: + type: ClusterIP + ports: + - port: 8000 + targetPort: 8000 + protocol: TCP + selector: + app: mineru diff --git a/deployment/kubernetes/mysql/configmap.yaml b/deployment/kubernetes/mysql/configmap.yaml new file mode 100644 index 0000000..12c9cb0 --- /dev/null +++ b/deployment/kubernetes/mysql/configmap.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysql-utf8-config +data: + utf8.cnf: | + [mysqld] + # 设置服务器默认字符集为 utf8mb4 (推荐,支持完整的 UTF-8,包括 emoji) + character-set-server = utf8mb4 + # 设置默认排序规则 + collation-server = utf8mb4_unicode_ci + # 或者使用 utf8_general_ci (性能稍好,但排序规则稍宽松) + default-time-zone = 'Asia/Shanghai' + + [client] + # 设置客户端连接默认字符集 + default-character-set = utf8mb4 + + [mysql] + # 设置 mysql 命令行客户端默认字符集 + default-character-set = utf8mb4 \ No newline at end of file diff --git a/deployment/kubernetes/mysql/deploy.yaml b/deployment/kubernetes/mysql/deploy.yaml new file mode 100644 index 0000000..a50bcc2 --- /dev/null +++ b/deployment/kubernetes/mysql/deploy.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysql + labels: + app: mysql +spec: + replicas: 1 + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app: mysql + spec: + containers: + - name: mysql + image: mysql:8 + imagePullPolicy: IfNotPresent + env: + - name: MYSQL_ROOT_PASSWORD + value: "Huawei@123" + ports: + - containerPort: 3306 + volumeMounts: + - name: data-volume + mountPath: /var/lib/mysql + - name: init-sql + mountPath: /docker-entrypoint-initdb.d + - name: mysql-utf8-config + mountPath: /etc/mysql/conf.d + volumes: + - name: data-volume + hostPath: + path: /opt/data-mate/data/mysql + type: DirectoryOrCreate + - name: init-sql + configMap: + name: init-sql + - name: mysql-utf8-config + configMap: + name: mysql-utf8-config + +--- +apiVersion: v1 +kind: Service +metadata: + name: mysql + labels: + app: mysql +spec: + type: ClusterIP + ports: + - port: 3306 + targetPort: 3306 + protocol: TCP + selector: + app: mysql diff --git a/deployment/kubernetes/unstructured/deploy.yaml b/deployment/kubernetes/unstructured/deploy.yaml new file mode 100644 index 0000000..a57555b --- /dev/null +++ b/deployment/kubernetes/unstructured/deploy.yaml @@ -0,0 +1,49 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: unstructured + labels: + app: unstructured +spec: + replicas: 1 + selector: + matchLabels: + app: unstructured + template: + metadata: + labels: + app: unstructured + spec: + containers: + - name: unstructured + image: unstructured + imagePullPolicy: IfNotPresent + command: + - python + args: + - app.py + ports: + - containerPort: 8000 + volumeMounts: + - name: tmp + mountPath: /tmp/data-mate + volumes: + - name: tmp + hostPath: + path: /tmp/data-mate + +--- +apiVersion: v1 +kind: Service +metadata: + name: unstructured + labels: + app: unstructured +spec: + type: ClusterIP + ports: + - port: 8000 + targetPort: 8000 + protocol: TCP + selector: + app: unstructured diff --git a/editions/community/config/application.yml b/editions/community/config/application.yml new file mode 100644 index 0000000..c43477a --- /dev/null +++ b/editions/community/config/application.yml @@ -0,0 +1,181 @@ +# 数据引擎平台 - 主应用配置 +spring: + application: + name: data-mate-platform + + # 暂时排除Spring Security自动配置(开发阶段使用) + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration + - org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration + + # 数据源配置 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://mysql:3306/datamate?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: ${DB_USERNAME:root} + password: ${DB_PASSWORD:Huawei@123} + hikari: + maximum-pool-size: 20 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + + # Elasticsearch配置 + elasticsearch: + uris: ${ES_URIS:http://localhost:9200} + username: ${ES_USERNAME:} + password: ${ES_PASSWORD:} + connection-timeout: 10s + socket-timeout: 30s + + # Jackson配置 + jackson: + time-zone: Asia/Shanghai + date-format: yyyy-MM-dd HH:mm:ss + serialization: + write-dates-as-timestamps: false + deserialization: + fail-on-unknown-properties: false + + # 文件上传配置 + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + + # 任务调度配置 + task: + execution: + pool: + core-size: ${TASK_EXECUTION_CORE_SIZE:10} + max-size: ${TASK_EXECUTION_MAX_SIZE:20} + queue-capacity: ${TASK_EXECUTION_QUEUE_CAPACITY:100} + keep-alive: ${TASK_EXECUTION_KEEP_ALIVE:60s} + scheduling: + pool: + size: ${TASK_SCHEDULING_POOL_SIZE:5} + config: + import: + - classpath:config/application-datacollection.yml + - classpath:config/application-datamanagement.yml + +# MyBatis配置(需在顶层,不在 spring 下) +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + default-fetch-size: 100 + default-statement-timeout: 30 + use-generated-keys: true + cache-enabled: true + lazy-loading-enabled: false + multiple-result-sets-enabled: true + use-column-label: true + auto-mapping-behavior: partial + auto-mapping-unknown-column-behavior: none + default-executor-type: simple + call-setters-on-nulls: false + return-instance-for-empty-row: false + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + mapper-locations: + - classpath*:mappers/**/*.xml + type-aliases-package: + - com.datamate.collection.domain.model + - com.datamate.datamanagement.domain.model.dataset + +# 应用配置 +server: + port: ${SERVER_PORT:8080} + servlet: + context-path: /api + encoding: + charset: UTF-8 + enabled: true + force: true + +# 日志配置 +logging: + config: file:/opt/backend/log4j2.xml + +# Actuator配置 +management: + endpoints: + web: + exposure: + include: health,info,metrics,prometheus + endpoint: + health: + show-details: when-authorized + health: + elasticsearch: + enabled: false # 禁用Elasticsearch健康检查 + +# 平台配置 +datamate: + # JWT配置 + jwt: + secret: ${JWT_SECRET:dataMateSecretKey2024ForJWTTokenGeneration} + expiration: ${JWT_EXPIRATION:86400} # 24小时,单位秒 + header: Authorization + prefix: "Bearer " + + # 文件存储配置 + storage: + type: ${STORAGE_TYPE:local} # local, minio, s3 + local: + base-path: ${STORAGE_LOCAL_PATH:./data/storage} + minio: + endpoint: ${MINIO_ENDPOINT:http://localhost:9000} + access-key: ${MINIO_ACCESS_KEY:minioadmin} + secret-key: ${MINIO_SECRET_KEY:minioadmin} + bucket-name: ${MINIO_BUCKET:data-mate} + + # Ray执行器配置 + ray: + enabled: ${RAY_ENABLED:false} + address: ${RAY_ADDRESS:ray://localhost:10001} + runtime-env: + working-dir: ${RAY_WORKING_DIR:./runtime/python-executor} + pip-packages: + - "ray[default]==2.7.0" + - "pandas" + - "numpy" + - "data-juicer" + + # 数据归集服务配置(可由模块导入叠加) + data-collection: {} + + # 算子市场配置 + operator-market: + repository-path: ${OPERATOR_REPO_PATH:./runtime/operators} + registry-url: ${OPERATOR_REGISTRY_URL:} + max-upload-size: ${OPERATOR_MAX_UPLOAD_SIZE:50MB} + + # 数据处理配置 + data-processing: + max-file-size: ${MAX_FILE_SIZE:1GB} + temp-dir: ${TEMP_DIR:./data/temp} + batch-size: ${BATCH_SIZE:1000} + + # 标注配置 + annotation: + auto-annotation: + enabled: ${AUTO_ANNOTATION_ENABLED:true} + model-endpoint: ${ANNOTATION_MODEL_ENDPOINT:} + quality-control: + enabled: ${QC_ENABLED:true} + threshold: ${QC_THRESHOLD:0.8} + + # RAG配置 + rag: + embedding: + model: ${RAG_EMBEDDING_MODEL:text-embedding-ada-002} + api-key: ${RAG_API_KEY:} + dimension: ${RAG_DIMENSION:1536} + chunk: + size: ${RAG_CHUNK_SIZE:512} + overlap: ${RAG_CHUNK_OVERLAP:50} + retrieval: + top-k: ${RAG_TOP_K:5} + score-threshold: ${RAG_SCORE_THRESHOLD:0.7} diff --git a/editions/community/config/log4j2.xml b/editions/community/config/log4j2.xml new file mode 100644 index 0000000..f9d0cf3 --- /dev/null +++ b/editions/community/config/log4j2.xml @@ -0,0 +1,42 @@ + + + + /var/log/data-mate/backend + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n + 100MB + 30 + INFO + WARN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/editions/enterprise/config/application.yml b/editions/enterprise/config/application.yml new file mode 100644 index 0000000..c43477a --- /dev/null +++ b/editions/enterprise/config/application.yml @@ -0,0 +1,181 @@ +# 数据引擎平台 - 主应用配置 +spring: + application: + name: data-mate-platform + + # 暂时排除Spring Security自动配置(开发阶段使用) + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration + - org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration + + # 数据源配置 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://mysql:3306/datamate?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: ${DB_USERNAME:root} + password: ${DB_PASSWORD:Huawei@123} + hikari: + maximum-pool-size: 20 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + + # Elasticsearch配置 + elasticsearch: + uris: ${ES_URIS:http://localhost:9200} + username: ${ES_USERNAME:} + password: ${ES_PASSWORD:} + connection-timeout: 10s + socket-timeout: 30s + + # Jackson配置 + jackson: + time-zone: Asia/Shanghai + date-format: yyyy-MM-dd HH:mm:ss + serialization: + write-dates-as-timestamps: false + deserialization: + fail-on-unknown-properties: false + + # 文件上传配置 + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + + # 任务调度配置 + task: + execution: + pool: + core-size: ${TASK_EXECUTION_CORE_SIZE:10} + max-size: ${TASK_EXECUTION_MAX_SIZE:20} + queue-capacity: ${TASK_EXECUTION_QUEUE_CAPACITY:100} + keep-alive: ${TASK_EXECUTION_KEEP_ALIVE:60s} + scheduling: + pool: + size: ${TASK_SCHEDULING_POOL_SIZE:5} + config: + import: + - classpath:config/application-datacollection.yml + - classpath:config/application-datamanagement.yml + +# MyBatis配置(需在顶层,不在 spring 下) +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + default-fetch-size: 100 + default-statement-timeout: 30 + use-generated-keys: true + cache-enabled: true + lazy-loading-enabled: false + multiple-result-sets-enabled: true + use-column-label: true + auto-mapping-behavior: partial + auto-mapping-unknown-column-behavior: none + default-executor-type: simple + call-setters-on-nulls: false + return-instance-for-empty-row: false + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + mapper-locations: + - classpath*:mappers/**/*.xml + type-aliases-package: + - com.datamate.collection.domain.model + - com.datamate.datamanagement.domain.model.dataset + +# 应用配置 +server: + port: ${SERVER_PORT:8080} + servlet: + context-path: /api + encoding: + charset: UTF-8 + enabled: true + force: true + +# 日志配置 +logging: + config: file:/opt/backend/log4j2.xml + +# Actuator配置 +management: + endpoints: + web: + exposure: + include: health,info,metrics,prometheus + endpoint: + health: + show-details: when-authorized + health: + elasticsearch: + enabled: false # 禁用Elasticsearch健康检查 + +# 平台配置 +datamate: + # JWT配置 + jwt: + secret: ${JWT_SECRET:dataMateSecretKey2024ForJWTTokenGeneration} + expiration: ${JWT_EXPIRATION:86400} # 24小时,单位秒 + header: Authorization + prefix: "Bearer " + + # 文件存储配置 + storage: + type: ${STORAGE_TYPE:local} # local, minio, s3 + local: + base-path: ${STORAGE_LOCAL_PATH:./data/storage} + minio: + endpoint: ${MINIO_ENDPOINT:http://localhost:9000} + access-key: ${MINIO_ACCESS_KEY:minioadmin} + secret-key: ${MINIO_SECRET_KEY:minioadmin} + bucket-name: ${MINIO_BUCKET:data-mate} + + # Ray执行器配置 + ray: + enabled: ${RAY_ENABLED:false} + address: ${RAY_ADDRESS:ray://localhost:10001} + runtime-env: + working-dir: ${RAY_WORKING_DIR:./runtime/python-executor} + pip-packages: + - "ray[default]==2.7.0" + - "pandas" + - "numpy" + - "data-juicer" + + # 数据归集服务配置(可由模块导入叠加) + data-collection: {} + + # 算子市场配置 + operator-market: + repository-path: ${OPERATOR_REPO_PATH:./runtime/operators} + registry-url: ${OPERATOR_REGISTRY_URL:} + max-upload-size: ${OPERATOR_MAX_UPLOAD_SIZE:50MB} + + # 数据处理配置 + data-processing: + max-file-size: ${MAX_FILE_SIZE:1GB} + temp-dir: ${TEMP_DIR:./data/temp} + batch-size: ${BATCH_SIZE:1000} + + # 标注配置 + annotation: + auto-annotation: + enabled: ${AUTO_ANNOTATION_ENABLED:true} + model-endpoint: ${ANNOTATION_MODEL_ENDPOINT:} + quality-control: + enabled: ${QC_ENABLED:true} + threshold: ${QC_THRESHOLD:0.8} + + # RAG配置 + rag: + embedding: + model: ${RAG_EMBEDDING_MODEL:text-embedding-ada-002} + api-key: ${RAG_API_KEY:} + dimension: ${RAG_DIMENSION:1536} + chunk: + size: ${RAG_CHUNK_SIZE:512} + overlap: ${RAG_CHUNK_OVERLAP:50} + retrieval: + top-k: ${RAG_TOP_K:5} + score-threshold: ${RAG_SCORE_THRESHOLD:0.7} diff --git a/editions/enterprise/config/log4j2.xml b/editions/enterprise/config/log4j2.xml new file mode 100644 index 0000000..f9d0cf3 --- /dev/null +++ b/editions/enterprise/config/log4j2.xml @@ -0,0 +1,42 @@ + + + + /var/log/data-mate/backend + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n + 100MB + 30 + INFO + WARN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..2e8f378 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +src/mock/sessions/* + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.vite \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..b4fcd04 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,96 @@ +🚀 快速开始 + +``` +npm install # 安装依赖 +npm run dev # 启动项目 +npm run mock # 启动后台Mock服务(可选) +``` + +📁 项目结构 + +``` +frontend/ +├── public/ # 📖 文档中心 +│ ├── huawei-logo.webp/ # logo +│ └── xxx/ # 标注工作台(可分离部署) +│ +├── src/ # 🎨 前端应用 +│ ├── apps/ # 多前端应用 +│ │ ├── console/ # 数据工作台&运营控制台 +│ │ │ ├── next.config.js +│ │ │ ├── package.json +│ │ │ └── src/ +│ │ └── annotation-studio/ # 标注工作台(可分离部署) +│ │ +│ ├── assets/ # 共享UI组件/SDK +│ │ ├── xxx/ # 数据工作台&运营控制台 +│ │ │ ├── next.config.js +│ │ │ └── src/ +│ │ │ +│ │ │ +│ │ └── xxx/ # 数据工作台&运营控制台 +│ │ ├── package.json +│ │ └── src/ +│ │ +│ ├── components/ # 构建与环境配置 +│ │ ├── CardView.tsx # 数据工作台&运营控制台 +│ │ ├── DetailHeader.tsx # 数据工作台&运营控制台 +│ │ ├── RadioCard.tsx # 数据工作台&运营控制台 +│ │ ├── SearchControls # 数据工作台&运营控制台 +│ │ ├── TagList # 标注工作台(可分离部署) +│ │ └── TaskPopover # 标注工作台(可分离部署) +│ │ +│ ├── hooks/ # 构建与环境配置 +│ │ ├── console/ # 数据工作台&运营控制台 +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ └── annotation-studio/ # 标注工作台(可分离部署) +│ │ +│ ├── mock/ # 构建与环境配置 +│ │ ├── console/ # 数据工作台&运营控制台 +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ └── annotation-studio/ # 标注工作台(可分离部署) +│ │ +│ ├── pages/ # 构建与环境配置 +│ │ ├── console/ # 数据工作台&运营控制台 +│ │ │ ├── next.config.js +│ │ │ ├── package.json +│ │ │ └── src/ +│ │ └── annotation-studio/ # 标注工作台(可分离部署) +│ │ +│ ├── providers/ # 构建与环境配置 +│ │ ├── console/ # 数据工作台&运营控制台 +│ │ │ ├── next.config.js +│ │ │ ├── package.json +│ │ │ └── src/ +│ │ └── annotation-studio/ # 标注工作台(可分离部署) +│ │ +│ ├── routes/ # 构建与环境配置 +│ │ └── next.config.js +│ │ +│ ├── types/ # 构建与环境配置 +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ ├── next.config.js +│ │ └── next.config.js +│ │ +│ └── utils/ # 构建与环境配置 +│ ├── next.config.js +│ ├── next.config.js +│ └── next.config.js +│ +├── eslint.config.js/ # 🔧 后端服务架构 +├── index.html/ # 🔧 后端服务架构 +├── package.json/ # 🔧 后端服务架构 +├── README.md # 项目说明 +├── tailwind.config.ts # 更新日志 +├── vite.config.ts # 开源协议 +└── pom.xml # Maven根配置 +``` diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..d94e7de --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { globalIgnores } from 'eslint/config' + +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..9370cde --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + ML Dataset Tool + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..e981dff --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,7125 @@ +{ + "name": "edatamate", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "edatamate", + "version": "0.0.0", + "dependencies": { + "@xyflow/react": "^12.8.3", + "antd": "^5.27.0", + "jssha": "^3.3.1", + "lucide-react": "^0.539.0", + "react": "^18.1.1", + "react-dom": "^18.1.1", + "react-router": "^7.8.0", + "recharts": "2.15.0" + }, + "devDependencies": { + "@eslint/js": "^9.33.0", + "@tailwindcss/vite": "^4.1.12", + "@types/node": "^24.2.1", + "@types/react": "^18.1.10", + "@types/react-dom": "^18.1.7", + "@vitejs/plugin-react": "^5.0.0", + "body-parser": "^2.2.0", + "eslint": "^9.33.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "express": "^5.1.0", + "express-session": "^1.18.2", + "fs-extra": "^11.3.1", + "globals": "^16.3.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "mockjs": "^1.1.0", + "nodemon": "^3.1.10", + "postcss": "^8.5.6", + "session-file-store": "^1.5.0", + "tailwindcss": "^4.1.12", + "typescript": "~5.8.3", + "typescript-eslint": "^8.39.1", + "vite": "^7.1.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ant-design/colors": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz", + "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^2.0.6" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz", + "integrity": "sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "classnames": "^2.3.1", + "csstype": "^3.1.3", + "rc-util": "^5.35.0", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz", + "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz", + "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/react-slick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "resize-observer-polyfill": "^1.5.1", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", + "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rc-component/async-validator": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", + "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz", + "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^2.0.6", + "@babel/runtime": "^7.23.6", + "classnames": "^2.2.6", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz", + "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", + "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", + "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz", + "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz", + "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.0.tgz", + "integrity": "sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.44.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.30", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.30.tgz", + "integrity": "sha512-whXaSoNUFiyDAjkUF8OBpOm77Szdbk5lGNqFe6CbVbJFrhCCPinCbRA3NjawwlNHla1No7xvXXh+CpSxnPfUEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz", + "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.12" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz", + "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-x64": "4.1.12", + "@tailwindcss/oxide-freebsd-x64": "4.1.12", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-x64-musl": "4.1.12", + "@tailwindcss/oxide-wasm32-wasi": "4.1.12", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz", + "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz", + "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz", + "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz", + "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz", + "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz", + "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz", + "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz", + "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz", + "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz", + "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz", + "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz", + "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.12.tgz", + "integrity": "sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.12", + "@tailwindcss/oxide": "4.1.12", + "tailwindcss": "4.1.12" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", + "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.23", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", + "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", + "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/type-utils": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.39.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", + "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", + "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.0.tgz", + "integrity": "sha512-Jx9JfsTa05bYkS9xo0hkofp2dCmp1blrKjw9JONs5BTHOvJCgLbaPSuZLGSVJW6u2qe0tc4eevY0+gSNNi0YCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.30", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@xyflow/react": { + "version": "12.8.3", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.8.3.tgz", + "integrity": "sha512-8sdRZPMCzfhauF96krlUMPCKmi9cX64HsYG8qoVAAvTKDAqxXg7RSp/IhoXlzbI/lsRD1vAxeDBxvI/XqACa6g==", + "license": "MIT", + "dependencies": { + "@xyflow/system": "0.0.67", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.67.tgz", + "integrity": "sha512-hYsmbj+8JDei0jmupBmxNLaeJEcf9kKmMl6IziGe02i0TOCsHwjIdP+qz+f4rI1/FR2CQiCZJrw4dkHOLC6tEQ==", + "license": "MIT", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antd": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.27.0.tgz", + "integrity": "sha512-o54dmpooLOc08RSGCkeEQBYAGPxUSmnhmYJKCNTHH46vzjOVxdteu+wPTRVkRbAkDTbs2VcNr5VL7Lu67rPIiA==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.2.1", + "@ant-design/cssinjs": "^1.23.0", + "@ant-design/cssinjs-utils": "^1.1.3", + "@ant-design/fast-color": "^2.0.6", + "@ant-design/icons": "^5.6.1", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.26.0", + "@rc-component/color-picker": "~2.0.1", + "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.0.0", + "@rc-component/tour": "~1.15.1", + "@rc-component/trigger": "^2.3.0", + "classnames": "^2.5.1", + "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.11", + "rc-cascader": "~3.34.0", + "rc-checkbox": "~3.5.0", + "rc-collapse": "~3.9.0", + "rc-dialog": "~9.6.0", + "rc-drawer": "~7.3.0", + "rc-dropdown": "~4.2.1", + "rc-field-form": "~2.7.0", + "rc-image": "~7.12.0", + "rc-input": "~1.8.0", + "rc-input-number": "~9.5.0", + "rc-mentions": "~2.20.0", + "rc-menu": "~9.16.1", + "rc-motion": "^2.9.5", + "rc-notification": "~5.6.4", + "rc-pagination": "~5.1.0", + "rc-picker": "~4.11.3", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.1", + "rc-resize-observer": "^1.4.3", + "rc-segmented": "~2.7.0", + "rc-select": "~14.16.8", + "rc-slider": "~11.1.8", + "rc-steps": "~6.0.1", + "rc-switch": "~4.1.0", + "rc-table": "~7.51.1", + "rc-tabs": "~15.7.0", + "rc-textarea": "~1.10.2", + "rc-tooltip": "~6.4.0", + "rc-tree": "~5.13.1", + "rc-tree-select": "~5.27.0", + "rc-upload": "~4.9.2", + "rc-util": "^5.44.4", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/bagpipe": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/bagpipe/-/bagpipe-0.3.5.tgz", + "integrity": "sha512-42sAlmPDKes1nLm/aly+0VdaopSU9br+jkRELedhQxI5uXHgtk47I83Mpmf4zoNTRMASdLFtUkimlu/Z9zQ8+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", + "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001733", + "electron-to-chromium": "^1.5.199", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "license": "MIT" + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", + "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.200", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.200.tgz", + "integrity": "sha512-rFCxROw7aOe4uPTfIAx+rXv9cEcGx+buAF4npnhtTqCJk5KDFRnh3+KYj7rdVh6lsFt5/aPs+Irj9rZ33WMA7w==", + "dev": true, + "license": "ISC" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-session": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.1.0", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jssha": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz", + "integrity": "sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kruptein": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-2.2.3.tgz", + "integrity": "sha512-BTwprBPTzkFT9oTugxKd3WnWrX630MqUDsnmBuoa98eQs12oD4n4TeI0GbpdGcYn/73Xueg2rfnw+oK4dovnJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1.js": "^5.4.1" + }, + "engines": { + "node": ">6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.539.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.539.0.tgz", + "integrity": "sha512-VVISr+VF2krO91FeuCrm1rSOLACQUYVy7NQkzrOty52Y8TlTPcXcMdQFj9bYzBgXbWCiywlwSZ3Z8u6a+6bMlg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mockjs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mockjs/-/mockjs-1.1.0.tgz", + "integrity": "sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==", + "dev": true, + "dependencies": { + "commander": "*" + }, + "bin": { + "random": "bin/random" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rc-cascader": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz", + "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "^2.3.1", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-checkbox": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz", + "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.25.2" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-collapse": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz", + "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.3.4", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dialog": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz", + "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/portal": "^1.0.0-8", + "classnames": "^2.2.6", + "rc-motion": "^2.3.0", + "rc-util": "^5.21.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-drawer": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz", + "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@rc-component/portal": "^1.1.1", + "classnames": "^2.2.6", + "rc-motion": "^2.6.1", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dropdown": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz", + "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-util": "^5.44.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/rc-field-form": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.0.tgz", + "integrity": "sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/async-validator": "^5.0.3", + "rc-util": "^5.32.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-image": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz", + "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/portal": "^1.0.2", + "classnames": "^2.2.6", + "rc-dialog": "~9.6.0", + "rc-motion": "^2.6.2", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-input": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz", + "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.18.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-input-number": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz", + "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/mini-decimal": "^1.0.1", + "classnames": "^2.2.5", + "rc-input": "~1.8.0", + "rc-util": "^5.40.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-mentions": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz", + "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-input": "~1.8.0", + "rc-menu": "~9.16.0", + "rc-textarea": "~1.10.0", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-menu": { + "version": "9.16.1", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz", + "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.0.0", + "classnames": "2.x", + "rc-motion": "^2.4.3", + "rc-overflow": "^1.3.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-motion": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz", + "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.44.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-notification": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz", + "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.9.0", + "rc-util": "^5.20.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.4.1.tgz", + "integrity": "sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-pagination": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz", + "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-picker": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz", + "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.1", + "rc-overflow": "^1.3.2", + "rc-resize-observer": "^1.4.0", + "rc-util": "^5.43.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/rc-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-rate": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz", + "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz", + "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.44.1", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-segmented": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.0.tgz", + "integrity": "sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-select": { + "version": "14.16.8", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz", + "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-slider": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.8.tgz", + "integrity": "sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-steps": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz", + "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.16.7", + "classnames": "^2.2.3", + "rc-util": "^5.16.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-switch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz", + "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0", + "classnames": "^2.2.1", + "rc-util": "^5.30.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-table": { + "version": "7.51.1", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.51.1.tgz", + "integrity": "sha512-5iq15mTHhvC42TlBLRCoCBLoCmGlbRZAlyF21FonFnS/DIC8DeRqnmdyVREwt2CFbPceM0zSNdEeVfiGaqYsKw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/context": "^1.4.0", + "classnames": "^2.2.5", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.44.3", + "rc-virtual-list": "^3.14.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tabs": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.7.0.tgz", + "integrity": "sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "rc-dropdown": "~4.2.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.6.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.34.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-textarea": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.2.tgz", + "integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-input": "~1.8.0", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tooltip": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz", + "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.1", + "rc-util": "^5.44.3" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tree": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz", + "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-tree-select": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz", + "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "2.x", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-upload": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.9.2.tgz", + "integrity": "sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-virtual-list": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.1.tgz", + "integrity": "sha512-DCapO2oyPqmooGhxBuXHM4lFuX+sshQwWqqkuyFA+4rShLe//+GEPVwiDgO+jKtKHtbeYwZoNvetwfHdOf+iUQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.0.tgz", + "integrity": "sha512-r15M3+LHKgM4SOapNmsH3smAizWds1vJ0Z9C4mWaKnT9/wD7+d/0jYcj6LmOvonkrO4Rgdyp4KQ/29gWN2i1eg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.0.tgz", + "integrity": "sha512-cIvMxDfpAmqAmVgc4yb7pgm/O1tmmkl/CjrvXuW+62/+7jj/iF9Ykm+hb/UJt42TREHMyd3gb+pkgoa2MxgDIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/session-file-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/session-file-store/-/session-file-store-1.5.0.tgz", + "integrity": "sha512-60IZaJNzyu2tIeHutkYE8RiXVx3KRvacOxfLr2Mj92SIsRIroDsH0IlUUR6fJAjoTW4RQISbaOApa2IZpIwFdQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bagpipe": "^0.3.5", + "fs-extra": "^8.0.1", + "kruptein": "^2.0.4", + "object-assign": "^4.1.1", + "retry": "^0.12.0", + "write-file-atomic": "3.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/session-file-store/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/session-file-store/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/session-file-store/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", + "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.1.tgz", + "integrity": "sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.39.1", + "@typescript-eslint/parser": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dev": true, + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz", + "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..99161d0 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,49 @@ +{ + "name": "edatamate", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "mock": "cd src/mock && nodemon --config nodemon.json --inspect=0.0.0.0:9229 mock.cjs --env=development --port=8002", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@xyflow/react": "^12.8.3", + "antd": "^5.27.0", + "jssha": "^3.3.1", + "lucide-react": "^0.539.0", + "react": "^18.1.1", + "react-dom": "^18.1.1", + "react-router": "^7.8.0", + "recharts": "2.15.0" + }, + "devDependencies": { + "@eslint/js": "^9.33.0", + "@tailwindcss/vite": "^4.1.12", + "@types/node": "^24.2.1", + "@types/react": "^18.1.10", + "@types/react-dom": "^18.1.7", + "@vitejs/plugin-react": "^5.0.0", + "body-parser": "^2.2.0", + "eslint": "^9.33.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "express": "^5.1.0", + "express-session": "^1.18.2", + "fs-extra": "^11.3.1", + "globals": "^16.3.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "mockjs": "^1.1.0", + "nodemon": "^3.1.10", + "postcss": "^8.5.6", + "session-file-store": "^1.5.0", + "tailwindcss": "^4.1.12", + "typescript": "~5.8.3", + "typescript-eslint": "^8.39.1", + "vite": "^7.1.2" + } +} diff --git a/frontend/public/huawei-logo.webp b/frontend/public/huawei-logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..99f1c5de11eb407fc28211ac4c524967422ca65b GIT binary patch literal 19172 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 literal 0 HcmV?d00001 diff --git a/frontend/src/components/AddTagPopover.tsx b/frontend/src/components/AddTagPopover.tsx new file mode 100644 index 0000000..5de0deb --- /dev/null +++ b/frontend/src/components/AddTagPopover.tsx @@ -0,0 +1,129 @@ +import { Button, Input, Popover, theme, Tag } from "antd"; +import { PlusOutlined } from "@ant-design/icons"; +import { useEffect, useMemo, useState } from "react"; + +interface Tag { + id: number; + name: string; + color: string; +} + +interface AddTagPopoverProps { + tags: Tag[]; + onFetchTags?: () => Promise; + onAddTag?: (tag: Tag) => void; + onCreateAndTag?: (tagName: string) => void; +} + +export default function AddTagPopover({ + tags, + onFetchTags, + onAddTag, + onCreateAndTag, +}: AddTagPopoverProps) { + const { token } = theme.useToken(); + const [showPopover, setShowPopover] = useState(false); + + const [newTag, setNewTag] = useState(""); + const [allTags, setAllTags] = useState([]); + + const tagsSet = useMemo(() => new Set(tags.map((tag) => tag.id)), [tags]); + + const fetchTags = async () => { + if (onFetchTags && showPopover) { + const data = await onFetchTags?.(); + setAllTags(data || []); + } + }; + useEffect(() => { + fetchTags(); + }, [showPopover]); + + const availableTags = useMemo(() => { + return allTags.filter((tag) => !tagsSet.has(tag.id)); + }, [allTags, tagsSet]); + + const handleCreateAndAddTag = () => { + if (newTag.trim()) { + onCreateAndTag?.(newTag.trim()); + setNewTag(""); + } + + setShowPopover(false); + }; + + const tagPlusStyle: React.CSSProperties = { + height: 22, + background: token.colorBgContainer, + borderStyle: "dashed", + }; + + return ( + <> + +

+ 添加标签 +

+ {/* Available Tags */} +
+
选择现有标签
+
+ {availableTags.map((tag) => ( + { + onAddTag?.(tag.name); + setShowPopover(false); + }} + > + + {tag.name} + + ))} +
+
+ + {/* Create New Tag */} +
+
创建新标签
+
+ setNewTag(e.target.value)} + className="h-8 text-sm" + /> + +
+
+ + + + } + > + } + className="cursor-pointer" + onClick={() => setShowPopover(true)} + > + 添加标签 + +
+ + ); +} diff --git a/frontend/src/components/CardView.tsx b/frontend/src/components/CardView.tsx new file mode 100644 index 0000000..f230280 --- /dev/null +++ b/frontend/src/components/CardView.tsx @@ -0,0 +1,291 @@ +import React, { useState, useEffect, useRef } from "react"; +import { Tag, Pagination, Dropdown, Tooltip, Empty, Popover } from "antd"; +import { + EllipsisOutlined, + ClockCircleOutlined, + StarFilled, +} from "@ant-design/icons"; +import type { ItemType } from "antd/es/menu/interface"; +import { formatDateTime } from "@/utils/unit"; + +interface BaseCardDataType { + id: string | number; + name: string; + type: string; + icon?: React.JSX.Element; + iconColor?: string; + status: { + label: string; + icon?: React.JSX.Element; + color?: string; + } | null; + description: string; + tags?: string[]; + statistics?: { label: string; value: string | number }[]; + updatedAt?: string; +} + +interface CardViewProps { + data: T[]; + pagination: { + [key: string]: any; + current: number; + pageSize: number; + total: number; + }; + operations: + | { + key: string; + label: string; + icon?: React.JSX.Element; + onClick?: (item: T) => void; + }[] + | ((item: T) => ItemType[]); + onView?: (item: T) => void; + onFavorite?: (item: T) => void; + isFavorite?: (item: T) => boolean; +} + +// 标签渲染组件 +const TagsRenderer = ({ tags }: { tags?: any[] }) => { + const [visibleTags, setVisibleTags] = useState([]); + const [hiddenTags, setHiddenTags] = useState([]); + const containerRef = useRef(null); + + useEffect(() => { + if (!tags || tags.length === 0) return; + + const calculateVisibleTags = () => { + if (!containerRef.current) return; + + const containerWidth = containerRef.current.offsetWidth; + const tempDiv = document.createElement("div"); + tempDiv.style.visibility = "hidden"; + tempDiv.style.position = "absolute"; + tempDiv.style.top = "-9999px"; + tempDiv.className = "flex flex-wrap gap-1"; + document.body.appendChild(tempDiv); + + let totalWidth = 0; + let visibleCount = 0; + const tagElements: HTMLElement[] = []; + + // 为每个tag创建临时元素来测量宽度 + tags.forEach((tag, index) => { + const tagElement = document.createElement("span"); + tagElement.className = "ant-tag ant-tag-default"; + tagElement.style.margin = "2px"; + tagElement.textContent = typeof tag === "string" ? tag : tag.name; + tempDiv.appendChild(tagElement); + tagElements.push(tagElement); + + const tagWidth = tagElement.offsetWidth + 4; // 加上gap的宽度 + + // 如果不是最后一个标签,需要预留+n标签的空间 + const plusTagWidth = index < tags.length - 1 ? 35 : 0; // +n标签大约35px宽度 + + if (totalWidth + tagWidth + plusTagWidth <= containerWidth) { + totalWidth += tagWidth; + visibleCount++; + } else { + // 如果当前标签放不下,且已经有可见标签,则停止 + if (visibleCount > 0) return; + // 如果是第一个标签就放不下,至少显示一个 + if (index === 0) { + totalWidth += tagWidth; + visibleCount = 1; + } + } + }); + + document.body.removeChild(tempDiv); + + setVisibleTags(tags.slice(0, visibleCount)); + setHiddenTags(tags.slice(visibleCount)); + }; + + // 延迟执行以确保DOM已渲染 + const timer = setTimeout(calculateVisibleTags, 0); + + // 监听窗口大小变化 + const handleResize = () => { + calculateVisibleTags(); + }; + + window.addEventListener("resize", handleResize); + + return () => { + clearTimeout(timer); + window.removeEventListener("resize", handleResize); + }; + }, [tags]); + + if (!tags || tags.length === 0) return null; + + const popoverContent = ( +
+
+ {hiddenTags.map((tag, index) => ( + {typeof tag === "string" ? tag : tag.name} + ))} +
+
+ ); + + return ( +
+ {visibleTags.map((tag, index) => ( + {typeof tag === "string" ? tag : tag.name} + ))} + {hiddenTags.length > 0 && ( + + + +{hiddenTags.length} + + + )} +
+ ); +}; + +function CardView(props: CardViewProps) { + const { data, pagination, operations, onView, onFavorite, isFavorite } = + props; + + if (data.length === 0) { + return ( +
+ +
+ ); + } + + const ops = (item) => + typeof operations === "function" ? operations(item) : operations; + return ( +
+
+ {data.map((item) => ( +
+
+ {/* Header */} +
+
+ {item?.icon && ( +
+ {item?.icon} +
+ )} +
+
+

onView?.(item)} + > + {item?.name} +

+ {item?.status && ( + +
+ {item?.status?.icon} + {item?.status?.label} +
+
+ )} +
+
+
+ {onFavorite && ( + onFavorite?.(item)} + /> + )} +
+ +
+ {/* Tags */} + + + {/* Description */} +

+ + {item?.description} + +

+ + {/* Statistics */} +
+ {item?.statistics?.map((stat, idx) => ( +
+
+ {stat?.label}: +
+
+ {stat?.value} +
+
+ ))} +
+
+ + {/* Actions */} +
+
+
+ {" "} + {formatDateTime(item?.updatedAt)} +
+
+ {operations && ( + { + const operation = ops(item).find( + (op) => op.key === key + ); + if (operation?.onClick) { + operation.onClick(item); + } + }, + }} + > +
+ +
+
+ )} +
+
+
+ ))} +
+
+ +
+
+ ); +} + +export default CardView; diff --git a/frontend/src/components/DetailHeader.tsx b/frontend/src/components/DetailHeader.tsx new file mode 100644 index 0000000..dc14b63 --- /dev/null +++ b/frontend/src/components/DetailHeader.tsx @@ -0,0 +1,137 @@ +import React from "react"; +import { Database } from "lucide-react"; +import { Card, Dropdown, Button, Tag, Tooltip } from "antd"; +import type { ItemType } from "antd/es/menu/interface"; +import AddTagPopover from "./AddTagPopover"; + +interface StatisticItem { + icon: React.ReactNode; + label: string; + value: string | number; +} + +interface OperationItem { + key: string; + label: string; + icon?: React.ReactNode; + isDropdown?: boolean; + items?: ItemType[]; + onMenuClick?: (key: string) => void; + onClick?: () => void; + danger?: boolean; +} + +interface TagConfig { + showAdd: boolean; + tags: { id: number; name: string; color: string }[]; + onFetchTags?: () => Promise<{ + data: { id: number; name: string; color: string }[]; + }>; + onAddTag?: (tag: { id: number; name: string; color: string }) => void; + onCreateAndTag?: (tagName: string) => void; +} +interface DetailHeaderProps { + data: T; + statistics: StatisticItem[]; + operations: OperationItem[]; + tagConfig?: TagConfig; +} + +function DetailHeader({ + data, + statistics, + operations, + tagConfig, +}: DetailHeaderProps): React.ReactNode { + return ( + +
+
+
+ {data?.icon || } +
+
+
+

{data.name}

+ {data?.status && ( + +
+ {data.status?.icon} + {data.status?.label} +
+
+ )} +
+ {data?.tags && ( +
+ {data?.tags?.map((tag) => ( + + {tag.name} + + ))} + {tagConfig?.showAdd && ( + + )} +
+ )} +

{data.description}

+
+ {statistics.map((stat) => ( +
+ {stat.icon} + {stat.value} +
+ ))} +
+
+
+
+ {operations.map((op) => { + if (op.isDropdown) { + return ( + + +
+
+
+ ); +} + +export default DetailHeader; diff --git a/frontend/src/components/DevelopmentInProgress.tsx b/frontend/src/components/DevelopmentInProgress.tsx new file mode 100644 index 0000000..6b9a8c4 --- /dev/null +++ b/frontend/src/components/DevelopmentInProgress.tsx @@ -0,0 +1,27 @@ +import { Button } from "antd"; + +const DevelopmentInProgress = ({ showHome = true }) => { + return ( +
+
🚧
+

功能开发中

+

+ 为了给您带来更好的体验,我们计划2025.10.30 + 开放此功能 +

+ {showHome && ( + + )} +
+ ); +}; + +export default DevelopmentInProgress; diff --git a/frontend/src/components/ErrorBoundary.tsx b/frontend/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..6c7e2f9 --- /dev/null +++ b/frontend/src/components/ErrorBoundary.tsx @@ -0,0 +1,191 @@ +import React, { Component } from "react"; +import { Button, Modal } from "antd"; + +interface ErrorContextType { + hasError: boolean; + error: Error | null; + errorInfo: { componentStack: string } | null; +} + +const ErrorContext = React.createContext({ + hasError: false, + error: null, + errorInfo: null, +}); + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; + errorInfo: { componentStack: string } | null; + errorTimestamp: string | null; +} + +interface ErrorBoundaryProps { + children?: React.ReactNode; + onReset?: () => void; + showDetails?: boolean; +} + +export default class ErrorBoundary extends Component< + ErrorBoundaryProps, + ErrorBoundaryState +> { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { + hasError: false, + error: null, + errorInfo: null, + errorTimestamp: null, + }; + } + + static getDerivedStateFromError(error: any) { + // 更新 state 使下一次渲染能够显示降级 UI + return { + hasError: true, + error: error, + errorTimestamp: new Date().toISOString(), + }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + // 错误统计 + this.setState({ + error, + errorInfo, + hasError: true, + }); + + // 在实际应用中,这里可以集成错误报告服务 + this.logErrorToService(error, errorInfo); + + // 开发环境下在控制台显示详细错误 + if (process.env.NODE_ENV === "development") { + console.error("ErrorBoundary 捕获到错误:", error); + console.error("错误详情:", errorInfo); + } + } + + logErrorToService = (error: Error, errorInfo: React.ErrorInfo) => { + // 这里可以集成 Sentry、LogRocket 等错误监控服务 + const errorData = { + error: error.toString(), + errorInfo: errorInfo.componentStack, + timestamp: this.state.errorTimestamp, + url: window.location.href, + userAgent: navigator.userAgent, + }; + + // 模拟发送错误日志 + console.log("发送错误日志到监控服务:", errorData); + + // 实际使用时取消注释并配置您的错误监控服务 + /* + if (window.Sentry) { + window.Sentry.captureException(error, { extra: errorInfo }); + } + */ + }; + + handleReset = () => { + this.setState({ + hasError: false, + error: null, + errorInfo: null, + errorTimestamp: null, + }); + + // 可选:重新加载页面或执行其他恢复操作 + if (this.props.onReset) { + this.props.onReset(); + } + }; + + handleReload = () => { + window.location.reload(); + }; + + handleGoHome = () => { + window.location.href = "/"; + }; + + renderErrorDetails = () => { + const { error, errorInfo } = this.state; + + if (!this.props.showDetails) return null; + + return ( +
+
+ 错误信息: +
+            {error?.toString()}
+          
+
+ {errorInfo && ( +
+ 组件堆栈: +
+              {errorInfo.componentStack}
+            
+
+ )} +
+ ); + }; + + render() { + if (this.state.hasError) { + return ( + +
+
⚠️
+

出了点问题

+

应用程序遇到了意外错误。

+ +
+ + +
+ + {this.renderErrorDetails()} + +
+

+ 如果问题持续存在,请联系技术支持 +

+ + 错误 ID: {this.state.errorTimestamp} + +
+
+
+ ); + } + + return ( + + {this.props.children} + + ); + } +} + +export function withErrorBoundary( + Component: React.ComponentType +): React.ComponentType { + return (props) => ( + + + + ); +} diff --git a/frontend/src/components/RadioCard.tsx b/frontend/src/components/RadioCard.tsx new file mode 100644 index 0000000..99d9e4e --- /dev/null +++ b/frontend/src/components/RadioCard.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { Card } from "antd"; + +interface RadioCardOption { + value: string; + label: string; + description?: string; + icon?: SVGAElement | React.FC>; + color?: string; +} + +interface RadioCardProps { + options: RadioCardOption[]; + value: string; + onChange: (value: string) => void; + className?: string; +} + +const RadioCard: React.FC = ({ + options, + value, + onChange, + className, +}) => { + return ( +
+ {options.map((option) => ( +
onChange(option.value)} + > + +

+ {option.label} +

+ {option.description && ( +
+ {option.description} +
+ )} +
+ ))} +
+ ); +}; + +export default RadioCard; diff --git a/frontend/src/components/SearchControls.tsx b/frontend/src/components/SearchControls.tsx new file mode 100644 index 0000000..3e2c4d7 --- /dev/null +++ b/frontend/src/components/SearchControls.tsx @@ -0,0 +1,239 @@ +import { Input, Button, Select, Tag, Segmented, DatePicker } from "antd"; +import { + BarsOutlined, + AppstoreOutlined, + SearchOutlined, + ReloadOutlined, +} from "@ant-design/icons"; +import { useEffect, useState } from "react"; + +interface FilterOption { + key: string; + label: string; + mode?: "tags" | "multiple"; + options: { label: string; value: string }[]; +} + +interface SearchControlsProps { + searchTerm: string; + onSearchChange: (value: string) => void; + searchPlaceholder?: string; + + // Filter props + filters?: FilterOption[]; + selectedFilters?: Record; + onFiltersChange?: (filters: Record) => void; + onClearFilters?: () => void; + + // Date range props + dateRange?: [Date | null, Date | null] | null; + onDateChange?: (dates: [Date | null, Date | null] | null) => void; + + // Reload props + onReload?: () => void; + + // View props + viewMode?: "card" | "list"; + onViewModeChange?: (mode: "card" | "list") => void; + + // Control visibility + showFilters?: boolean; + showSort?: boolean; + showViewToggle?: boolean; + showReload?: boolean; + showDatePicker?: boolean; + + // Styling + className?: string; +} + +export function SearchControls({ + viewMode, + className, + searchTerm, + showFilters = true, + showViewToggle = true, + searchPlaceholder = "搜索...", + filters = [], + dateRange, + showDatePicker = false, + showReload = true, + onReload, + onDateChange, + onSearchChange, + onFiltersChange, + onViewModeChange, + onClearFilters, +}: SearchControlsProps) { + const [selectedFilters, setSelectedFilters] = useState<{ + [key: string]: string[]; + }>({}); + + const filtersMap: Record = filters.reduce( + (prev, cur) => ({ ...prev, [cur.key]: cur }), + {} + ); + + // select change + const handleFilterChange = (filterKey: string, value: string) => { + const filteredValues = { + ...selectedFilters, + [filterKey]: !value ? [] : [value], + }; + setSelectedFilters(filteredValues); + }; + + // 清除已选筛选 + const handleClearFilter = (filterKey: string, value: string | string[]) => { + const isMultiple = filtersMap[filterKey]?.mode === "multiple"; + if (!isMultiple) { + setSelectedFilters({ + ...selectedFilters, + [filterKey]: [], + }); + } else { + const currentValues = selectedFilters[filterKey]?.[0] || []; + const newValues = currentValues.filter((v) => v !== value); + setSelectedFilters({ + ...selectedFilters, + [filterKey]: [newValues], + }); + } + }; + + const handleClearAllFilters = () => { + setSelectedFilters({}); + onClearFilters?.(); + }; + + const hasActiveFilters = Object.values(selectedFilters).some( + (values) => values?.[0]?.length > 0 + ); + + useEffect(() => { + if (Object.keys(selectedFilters).length === 0) return; + onFiltersChange?.(selectedFilters); + }, [selectedFilters]); + + return ( +
+
+ {/* Left side - Search and Filters */} +
+ {/* Search */} +
+ onSearchChange(e.target.value)} + prefix={} + /> +
+ + {/* Filters */} + {showFilters && filters.length > 0 && ( +
+ {filters.map((filter: FilterOption) => ( + + ))} +
+ )} +
+ + {showDatePicker && ( + + )} + + {/* Right side */} +
+ {showViewToggle && onViewModeChange && ( + }, + { value: "card", icon: }, + ]} + value={viewMode} + onChange={(value) => onViewModeChange(value as "list" | "card")} + /> + )} + + {showReload && ( + + )} +
+
+ + {/* Active Filters Display */} + {hasActiveFilters && ( +
+
+
+ + 已选筛选: + + {Object.entries(selectedFilters).map(([filterKey, values]) => + values.map((value) => { + const filter = filtersMap[filterKey]; + + const getLabeledValue = (item: string) => { + const option = filter?.options.find( + (o) => o.value === item + ); + return ( + handleClearFilter(filterKey, item)} + color="blue" + > + {filter?.label}: {option?.label || item} + + ); + }; + return Array.isArray(value) + ? value.map((item) => getLabeledValue(item)) + : getLabeledValue(value); + }) + )} +
+ + {/* Clear all filters button on the right */} + +
+
+ )} +
+ ); +} diff --git a/frontend/src/components/TagList.tsx b/frontend/src/components/TagList.tsx new file mode 100644 index 0000000..75fa140 --- /dev/null +++ b/frontend/src/components/TagList.tsx @@ -0,0 +1,149 @@ +import React, { useEffect, useRef, useState } from "react"; +import { PlusOutlined } from "@ant-design/icons"; +import type { InputRef } from "antd"; +import { Flex, Input, Tag, theme, Tooltip } from "antd"; + +const tagInputStyle: React.CSSProperties = { + width: 64, + height: 22, + marginInlineEnd: 8, + verticalAlign: "top", +}; + +interface TagListProps { + tags: string[]; + setTags: (tags: string[]) => void; + onDelete?: (tag: string) => void; + onAdd?: (tag: string) => void; + onEdit?: (oldTag: string, newTag: string) => void; +} + +const TagList: React.FC = ({ + tags, + setTags, + onDelete, + onAdd, + onEdit, +}) => { + const { token } = theme.useToken(); + const [inputVisible, setInputVisible] = useState(false); + const [inputValue, setInputValue] = useState(""); + const [editInputIndex, setEditInputIndex] = useState(-1); + const [editInputValue, setEditInputValue] = useState(""); + const inputRef = useRef(null); + const editInputRef = useRef(null); + + useEffect(() => { + if (inputVisible) { + inputRef.current?.focus(); + } + }, [inputVisible]); + + useEffect(() => { + editInputRef.current?.focus(); + }, [editInputValue]); + + const handleClose = (removedTag: string) => { + const newTags = tags.filter((tag) => tag !== removedTag); + setTags(newTags); + onDelete?.(removedTag); + }; + + const showInput = () => { + setInputVisible(true); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + }; + + const handleInputConfirm = () => { + if (inputValue && !tags.includes(inputValue)) { + setTags([...tags, inputValue]); + onAdd?.(inputValue); + } + setInputVisible(false); + setInputValue(""); + }; + + const handleEditInputChange = (e: React.ChangeEvent) => { + setEditInputValue(e.target.value); + }; + + const handleEditInputConfirm = () => { + const newTags = [...tags]; + newTags[editInputIndex] = editInputValue; + setTags(newTags); + onEdit?.(tags[editInputIndex], editInputValue); + setEditInputIndex(-1); + setEditInputValue(""); + }; + + const tagPlusStyle: React.CSSProperties = { + height: 22, + background: token.colorBgContainer, + borderStyle: "dashed", + }; + + return ( + + {tags.map((tag, index) => { + if (editInputIndex === index) { + return ( + + ); + } + const isLongTag = tag.length > 20; + const tagElem = ( + handleClose(tag)} closable> + { + if (index !== 0) { + setEditInputIndex(index); + setEditInputValue(tag); + e.preventDefault(); + } + }} + > + {isLongTag ? `${tag.slice(0, 20)}...` : tag} + + + ); + return isLongTag ? ( + + {tagElem} + + ) : ( + tagElem + ); + })} + {inputVisible ? ( + + ) : ( + } onClick={showInput}> + 新增标签 + + )} + + ); +}; + +export default TagList; diff --git a/frontend/src/components/TagManagement.tsx b/frontend/src/components/TagManagement.tsx new file mode 100644 index 0000000..82a8bf9 --- /dev/null +++ b/frontend/src/components/TagManagement.tsx @@ -0,0 +1,271 @@ +import React, { useEffect, useState } from "react"; +import { Drawer, Input, Button, App } from "antd"; +import { PlusOutlined } from "@ant-design/icons"; +import { Edit, Save, TagIcon, X, Trash } from "lucide-react"; +import { TagItem } from "@/pages/DataManagement/dataset.model"; + +interface CustomTagProps { + isEditable?: boolean; + tag: { id: number; name: string }; + editingTag?: string | null; + editingTagValue?: string; + setEditingTag?: React.Dispatch>; + setEditingTagValue?: React.Dispatch>; + handleEditTag?: (tag: { id: number; name: string }, value: string) => void; + handleCancelEdit?: (tag: { id: number; name: string }) => void; + handleDeleteTag?: (tag: { id: number; name: string }) => void; +} + +function CustomTag({ + isEditable = false, + tag, + editingTag, + editingTagValue, + setEditingTag, + setEditingTagValue, + handleEditTag, + handleCancelEdit, + handleDeleteTag, +}: CustomTagProps) { + return ( +
+ {editingTag?.id === tag.id ? ( +
+ setEditingTagValue?.(e.target.value)} + onKeyPress={(e) => { + if (e.key === "Enter") { + handleEditTag?.(tag, editingTagValue); + } + if (e.key === "Escape") { + setEditingTag?.(null); + setEditingTagValue?.(""); + } + }} + className="h-6 text-sm" + autoFocus + /> +
+ ) : ( + <> + {tag.name} + {isEditable && ( +
+
+ )} + + )} +
+ ); +} + +export const mockPreparedTags = [ + { id: "1", name: "重要" }, + { id: "2", name: "待处理" }, + { id: "3", name: "已完成" }, + { id: "4", name: "审核中" }, + { id: "5", name: "高优先级" }, + { id: "6", name: "低优先级" }, + { id: "7", name: "客户A" }, + { id: "8", name: "客户B" }, +]; + +const TagManager: React.FC = ({ + onFetch, + onCreate, + onDelete, + onUpdate, +}: { + onFetch: () => Promise; + onCreate: (tag: Pick) => Promise<{ ok: boolean }>; + onDelete: (tagId: number) => Promise<{ ok: boolean }>; + onUpdate: (oldTagId: number, newTag: string) => Promise<{ ok: boolean }>; +}) => { + const [showTagManager, setShowTagManager] = useState(false); + const { message } = App.useApp(); + const [tags, setTags] = useState<{ id: number; name: string }[]>([]); + const [newTag, setNewTag] = useState(""); + const [editingTag, setEditingTag] = useState(null); + const [editingTagValue, setEditingTagValue] = useState(""); + + // 预置标签 + const [preparedTags, setPreparedTags] = useState(mockPreparedTags); + + // 获取标签列表 + const fetchTags = async () => { + if (!onFetch) return; + try { + const { data } = await onFetch?.(); + setTags(data || []); + } catch (e) { + message.error("获取标签失败"); + } + }; + + // 添加标签 + const addTag = async (tag: string) => { + try { + await onCreate?.({ + name: tag, + }); + fetchTags(); + message.success("标签添加成功"); + } catch (error) { + message.error("添加标签失败"); + } + }; + + // 删除标签 + const deleteTag = async (tag: TagItem) => { + try { + await onDelete?.(tag.id); + fetchTags(); + message.success("标签删除成功"); + } catch (error) { + message.error("删除标签失败"); + } + }; + + const updateTag = async (oldTag: TagItem, newTag: string) => { + try { + await onUpdate?.(oldTag.id, { ...oldTag, name: newTag }); + fetchTags(); + message.success("标签更新成功"); + } catch (error) { + message.error("更新标签失败"); + } + }; + + const handleCreateNewTag = () => { + if (newTag.trim()) { + addTag(newTag.trim()); + setNewTag(""); + } + }; + + const handleEditTag = (tag: TagItem, value: string) => { + if (value.trim()) { + updateTag(tag, value.trim()); + setEditingTag(null); + setEditingTagValue(""); + } + }; + + const handleCancelEdit = (tag: string) => { + setEditingTag(null); + setEditingTagValue(""); + }; + + const handleDeleteTag = (tag: TagItem) => { + deleteTag(tag); + setEditingTag(null); + setEditingTagValue(""); + }; + + useEffect(() => { + if (showTagManager) fetchTags(); + }, [showTagManager]); + + return ( + <> + + setShowTagManager(false)} + title="标签管理" + width={500} + > +
+ {/* Add New Tag */} +
+
+ setNewTag(e.target.value)} + onKeyPress={(e) => { + if (e.key === "Enter") { + addTag(e.target.value); + } + }} + /> + +
+
+ +

预置标签

+
+ {preparedTags.length > 0 && + preparedTags.map((tag) => )} +
+ +

自定义标签

+
+ {tags.map((tag) => ( + + ))} +
+
+
+ + ); +}; + +export default TagManager; diff --git a/frontend/src/components/TaskPopover.tsx b/frontend/src/components/TaskPopover.tsx new file mode 100644 index 0000000..768c796 --- /dev/null +++ b/frontend/src/components/TaskPopover.tsx @@ -0,0 +1,162 @@ +import { Button, Popover, Progress } from "antd"; +import { Calendar, Clock, Play, Trash2, X } from "lucide-react"; + +interface TaskItem { + id: string; + name: string; + status: string; + progress: number; + scheduleConfig: { + type: string; + cronExpression?: string; + executionCount?: number; + maxExecutions?: number; + }; + nextExecution?: string; + importConfig: { + source?: string; + }; + createdAt: string; +} + +export default function TaskPopover() { + const tasks: TaskItem[] = [ + { + id: "1", + name: "导入客户数据", + status: "importing", + progress: 65, + scheduleConfig: { + type: "manual", + }, + importConfig: { + source: "local", + }, + createdAt: "2025-07-29 14:23", + }, + { + id: "2", + name: "定时同步订单", + status: "waiting", + progress: 0, + scheduleConfig: { + type: "scheduled", + cronExpression: "0 0 * * *", + executionCount: 3, + maxExecutions: 10, + }, + nextExecution: "2025-07-31 00:00", + importConfig: { + source: "api", + }, + createdAt: "2025-07-28 09:10", + }, + { + id: "3", + name: "清理历史日志", + status: "finished", + progress: 100, + scheduleConfig: { + type: "manual", + }, + importConfig: { + source: "system", + }, + createdAt: "2025-07-27 17:45", + }, + ]; + + return ( + +
+

近期任务

+ +
+ +
+ {tasks.length === 0 ? ( +
+ +

暂无创建任务

+
+ ) : ( +
+ {tasks.map((task) => ( +
+
+
+

+ {task.name} +

+
+ {task.status === "waiting" && ( + + )} + +
+
+ + {task.status === "importing" && ( +
+
+ 导入进度 + {Math.round(task.progress)}% +
+ +
+ )} + + {/* Schedule Information */} + {task.scheduleConfig.type === "scheduled" && ( +
+
+ + 定时任务 +
+
Cron: {task.scheduleConfig.cronExpression}
+ {task.nextExecution && ( +
下次执行: {task.nextExecution}
+ )} +
+ 执行次数: {task.scheduleConfig.executionCount || 0}/ + {task.scheduleConfig.maxExecutions || 10} +
+
+ )} + +
+ + {task.importConfig.source === "local" + ? "本地上传" + : task.importConfig.source || "未知来源"} + + {task.createdAt} +
+
+
+ ))} +
+ )} +
+ + } + > + +
+ ); +} diff --git a/frontend/src/components/TopLoadingBar.tsx b/frontend/src/components/TopLoadingBar.tsx new file mode 100644 index 0000000..ec95fd1 --- /dev/null +++ b/frontend/src/components/TopLoadingBar.tsx @@ -0,0 +1,69 @@ +import { useEffect, useRef, useState } from "react"; + +const TopLoadingBar = () => { + const [isVisible, setIsVisible] = useState(false); + const [progress, setProgress] = useState(0); + const intervalRef = useRef(null); + + useEffect(() => { + // 监听全局事件 + const handleShow = () => { + setIsVisible(true); + setProgress(0); + + // 清除可能存在的旧interval + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + + // 模拟进度 + let currentProgress = 0; + intervalRef.current = setInterval(() => { + currentProgress += Math.random() * 10; + if (currentProgress >= 90) { + clearInterval(intervalRef.current); + } + setProgress(currentProgress); + }, 200); + }; + + const handleHide = () => { + // 清除进度interval + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + setProgress(100); + setTimeout(() => { + setIsVisible(false); + setProgress(0); + }, 300); + }; + + // 添加全局事件监听器 + window.addEventListener("loading:show", handleShow); + window.addEventListener("loading:hide", handleHide); + + return () => { + // 组件卸载时清理 + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + window.removeEventListener("loading:show", handleShow); + window.removeEventListener("loading:hide", handleHide); + }; + }, []); + + if (!isVisible) return null; + + return ( +
+
+
+ ); +}; + +export default TopLoadingBar; diff --git a/frontend/src/hooks/useDebouncedEffect.ts b/frontend/src/hooks/useDebouncedEffect.ts new file mode 100644 index 0000000..dfd56aa --- /dev/null +++ b/frontend/src/hooks/useDebouncedEffect.ts @@ -0,0 +1,17 @@ +import { useEffect } from "react"; + +export function useDebouncedEffect( + cb: () => void, + deps: any[] = [], + delay: number = 300 +) { + useEffect(() => { + const handler = setTimeout(() => { + cb(); + }, delay); + + return () => { + clearTimeout(handler); + }; + }, [...(deps || []), delay]); +} diff --git a/frontend/src/hooks/useFetchData.ts b/frontend/src/hooks/useFetchData.ts new file mode 100644 index 0000000..441eeba --- /dev/null +++ b/frontend/src/hooks/useFetchData.ts @@ -0,0 +1,113 @@ +// 首页数据获取 +import { useState } from "react"; +import { useDebouncedEffect } from "./useDebouncedEffect"; +import Loading from "@/utils/loading"; +import { App } from "antd"; + +export default function useFetchData( + fetchFunc: (params?: any) => Promise, + mapDataFunc: (data: any) => T = (data) => data as T +) { + const { message } = App.useApp(); + // 表格数据 + const [tableData, setTableData] = useState([]); + // 设置加载状态 + const [loading, setLoading] = useState(false); + + // 搜索参数 + const [searchParams, setSearchParams] = useState({ + keyword: "", + filter: { + type: [] as string[], + status: [] as string[], + tags: [] as string[], + }, + current: 1, + pageSize: 12, + }); + + // 分页配置 + const [pagination, setPagination] = useState({ + total: 0, + showSizeChanger: true, + pageSizeOptions: ["12", "24", "48"], + showTotal: (total: number) => `共 ${total} 条`, + onChange: (current: number, pageSize?: number) => { + setSearchParams((prev) => ({ + ...prev, + current, + pageSize: pageSize || prev.pageSize, + })); + }, + }); + + const handleFiltersChange = (searchFilters: { [key: string]: string[] }) => { + setSearchParams({ + ...searchParams, + current: 1, + filter: { ...searchParams.filter, ...searchFilters }, + }); + }; + + function getFirstOfArray(arr: string[]) { + if (!arr || arr.length === 0 || !Array.isArray(arr)) return undefined; + if (arr[0] === "all") return undefined; + return arr[0]; + } + + async function fetchData(extraParams = {}) { + const { keyword, filter, current, pageSize } = searchParams; + Loading.show(); + setLoading(true); + try { + const { data } = await fetchFunc({ + ...filter, + ...extraParams, + keyword, + type: getFirstOfArray(filter?.type) || undefined, + status: getFirstOfArray(filter?.status) || undefined, + tags: filter?.tags?.length ? filter.tags.join(",") : undefined, + page: current - 1, + size: pageSize, + }); + setPagination((prev) => ({ + ...prev, + total: data?.totalElements || 0, + })); + let result = []; + if (mapDataFunc) { + result = data?.content.map(mapDataFunc) ?? []; + } + setTableData(result); + } catch (error) { + console.error(error) + message.error("数据获取失败,请稍后重试"); + } finally { + Loading.hide(); + setLoading(false); + } + } + + useDebouncedEffect( + () => { + fetchData(); + }, + [searchParams], + searchParams?.keyword ? 500 : 0 + ); + + return { + loading, + tableData, + pagination: { + ...pagination, + current: searchParams.current, + pageSize: searchParams.pageSize, + }, + searchParams, + setSearchParams, + setPagination, + handleFiltersChange, + fetchData, + }; +} diff --git a/frontend/src/hooks/useLeavePrompt.ts b/frontend/src/hooks/useLeavePrompt.ts new file mode 100644 index 0000000..b7d0831 --- /dev/null +++ b/frontend/src/hooks/useLeavePrompt.ts @@ -0,0 +1,52 @@ +import { useCallback, useEffect, useState } from "react"; +import { useNavigate } from "react-router"; + +// 自定义hook:页面离开前提示 +export function useLeavePrompt(shouldPrompt: boolean) { + const navigate = useNavigate(); + const [showPrompt, setShowPrompt] = useState(false); + const [nextPath, setNextPath] = useState(null); + + // 浏览器刷新/关闭 + useEffect(() => { + const handler = (e: BeforeUnloadEvent) => { + if (shouldPrompt) { + e.preventDefault(); + e.returnValue = ""; + return ""; + } + }; + window.addEventListener("beforeunload", handler); + return () => window.removeEventListener("beforeunload", handler); + }, [shouldPrompt]); + + // 路由切换拦截 + useEffect(() => { + const unblock = (window as any).__REACT_ROUTER_DOM_HISTORY__?.block?.( + (tx: any) => { + if (shouldPrompt) { + setShowPrompt(true); + setNextPath(tx.location.pathname); + return false; + } + return true; + } + ); + return () => { + if (unblock) unblock(); + }; + }, [shouldPrompt]); + + const confirmLeave = useCallback(() => { + setShowPrompt(false); + if (nextPath) { + navigate(nextPath, { replace: true }); + } + }, [nextPath, navigate]); + + return { + showPrompt, + setShowPrompt, + confirmLeave, + }; +} diff --git a/frontend/src/hooks/useSearchParams.tsx b/frontend/src/hooks/useSearchParams.tsx new file mode 100644 index 0000000..3514d0a --- /dev/null +++ b/frontend/src/hooks/useSearchParams.tsx @@ -0,0 +1,18 @@ +import { useMemo } from "react"; +import { useLocation } from "react-router"; + +interface AnyObject { + [key: string]: any; +} + +export function useSearchParams(): AnyObject { + const { search } = useLocation(); + return useMemo(() => { + const urlParams = new URLSearchParams(search); + const params: AnyObject = {}; + for (const [key, value] of urlParams.entries()) { + params[key] = value; + } + return params; + }, [search]); +} diff --git a/frontend/src/hooks/useStyle.ts b/frontend/src/hooks/useStyle.ts new file mode 100644 index 0000000..5139240 --- /dev/null +++ b/frontend/src/hooks/useStyle.ts @@ -0,0 +1,20 @@ +import { createStyles } from "antd-style"; + +const useStyle = createStyles(({ css, token }) => { + const { antCls } = token; + return { + customTable: css` + ${antCls}-table { + ${antCls}-table-container { + ${antCls}-table-body, ${antCls}-table-content { + scrollbar-width: thin; + scrollbar-color: ${token.colorBorder} transparent; + scrollbar-gutter: stable; + } + } + } + `, + }; +}); + +export default useStyle; diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..5b2646d --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,45 @@ +@import "tailwindcss"; + +/* components/TopLoadingBar.css */ +.top-loading-bar { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 3px; + background-color: transparent; + z-index: 9999; + overflow: hidden; +} + +.loading-bar-progress { + height: 100%; + background: linear-gradient(90deg, #3498db, #2ecc71, #3498db); + background-size: 200% 100%; + animation: gradient-animation 2s linear infinite, width-animation 0.3s ease; + transition: width 0.3s ease; +} + +@keyframes gradient-animation { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +@keyframes width-animation { + from { + transform: translateX(-100%); + } + to { + transform: translateX(0); + } +} + +.show-task-popover { + opacity: 100%; + visibility: visible; + transform: translateX(0); +} \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..39407d8 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,18 @@ +import { StrictMode, Suspense } from "react"; +import { createRoot } from "react-dom/client"; +import { RouterProvider } from "react-router"; +import router from "./routes/routes"; +import { App as AntdApp, Spin } from "antd"; +import "./index.css"; +import TopLoadingBar from "./components/TopLoadingBar"; + +createRoot(document.getElementById("root")!).render( + + + }> + + + + + +); diff --git a/frontend/src/mock/annotation.tsx b/frontend/src/mock/annotation.tsx new file mode 100644 index 0000000..280815d --- /dev/null +++ b/frontend/src/mock/annotation.tsx @@ -0,0 +1,330 @@ +import { BarChart, Circle, Grid, ImageIcon, Layers, Maximize, MousePointer, Move, Square, Target, Crop, RotateCcw, FileText, Tag, Heart, HelpCircle, BookOpen, MessageSquare, Users, Zap, Globe, Scissors } from "lucide-react"; + +// Define the AnnotationTask type if not imported from elsewhere +interface AnnotationTask { + id: string + name: string + completed: string + completedCount: number + skippedCount: number + totalCount: number + annotators: Array<{ + id: string + name: string + avatar?: string + }> + text: string + status: "completed" | "in_progress" | "pending" | "skipped" + project: string + type: "图像分类" | "文本分类" | "目标检测" | "NER" | "语音识别" | "视频分析" + datasetType: "text" | "image" | "video" | "audio" + progress: number +} + + +export const mockTasks: AnnotationTask[] = [ + { + id: "12345678", + name: "图像分类标注任务", + completed: "2024年1月20日 20:40", + completedCount: 1, + skippedCount: 0, + totalCount: 2, + annotators: [ + { id: "1", name: "张三", avatar: "/placeholder-user.jpg" }, + { id: "2", name: "李四", avatar: "/placeholder-user.jpg" }, + ], + text: "对产品图像进行分类标注,包含10个类别", + status: "completed", + project: "图像分类", + type: "图像分类", + datasetType: "image", + progress: 100, + }, + { + id: "12345679", + name: "文本情感分析标注", + completed: "2024年1月20日 20:40", + completedCount: 2, + skippedCount: 0, + totalCount: 2, + annotators: [ + { id: "1", name: "王五", avatar: "/placeholder-user.jpg" }, + { id: "2", name: "赵六", avatar: "/placeholder-user.jpg" }, + ], + text: "对用户评论进行情感倾向标注", + status: "completed", + project: "文本分类", + type: "文本分类", + datasetType: "text", + progress: 100, + }, + { + id: "12345680", + name: "目标检测标注任务", + completed: "2024年1月20日 20:40", + completedCount: 1, + skippedCount: 0, + totalCount: 2, + annotators: [{ id: "1", name: "孙七", avatar: "/placeholder-user.jpg" }], + text: "对交通场景图像进行目标检测标注", + status: "in_progress", + project: "目标检测", + type: "目标检测", + datasetType: "image", + progress: 50, + }, + { + id: "12345681", + name: "命名实体识别标注", + completed: "2024年1月20日 20:40", + completedCount: 1, + skippedCount: 0, + totalCount: 2, + annotators: [{ id: "1", name: "周八", avatar: "/placeholder-user.jpg" }], + text: "对新闻文本进行命名实体识别标注", + status: "in_progress", + project: "NER", + type: "NER", + datasetType: "text", + progress: 75, + }, + { + id: "12345682", + name: "语音识别标注任务", + completed: "2024年1月20日 20:40", + completedCount: 1, + skippedCount: 0, + totalCount: 2, + annotators: [{ id: "1", name: "吴九", avatar: "/placeholder-user.jpg" }], + text: "对语音数据进行转录和标注", + status: "in_progress", + project: "语音识别", + type: "语音识别", + datasetType: "audio", + progress: 25, + }, + { + id: "12345683", + name: "视频动作识别标注", + completed: "2024年1月20日 20:40", + completedCount: 0, + skippedCount: 2, + totalCount: 2, + annotators: [ + { id: "1", name: "陈十", avatar: "/placeholder-user.jpg" }, + { id: "2", name: "林十一", avatar: "/placeholder-user.jpg" }, + ], + text: "对视频中的人体动作进行识别和标注", + status: "skipped", + project: "视频分析", + type: "视频分析", + datasetType: "video", + progress: 0, + }, +] + +// Define the Template type +type Template = { + id: string; + name: string; + category: string; + description: string; + type: string; + preview?: string; + icon: React.ReactNode; +}; + +// 扩展的预制模板数据 +export const mockTemplates: Template[] = [ + // 计算机视觉模板 + { + id: "cv-1", + name: "目标检测", + category: "Computer Vision", + description: "使用边界框标注图像中的目标对象", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Object+Detection", + icon: , + }, + { + id: "cv-2", + name: "语义分割(多边形)", + category: "Computer Vision", + description: "使用多边形精确标注图像中的区域", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Polygon+Segmentation", + icon: , + }, + { + id: "cv-3", + name: "语义分割(掩码)", + category: "Computer Vision", + description: "使用像素级掩码标注图像区域", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Mask+Segmentation", + icon: , + }, + { + id: "cv-4", + name: "关键点标注", + category: "Computer Vision", + description: "标注图像中的关键点位置", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Keypoint+Labeling", + icon: , + }, + { + id: "cv-5", + name: "图像分类", + category: "Computer Vision", + description: "为整个图像分配类别标签", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Image+Classification", + icon: , + }, + { + id: "cv-6", + name: "实例分割", + category: "Computer Vision", + description: "区分同类别的不同实例对象", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Instance+Segmentation", + icon: , + }, + { + id: "cv-7", + name: "全景分割", + category: "Computer Vision", + description: "结合语义分割和实例分割的全景标注", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Panoptic+Segmentation", + icon: , + }, + { + id: "cv-8", + name: "3D目标检测", + category: "Computer Vision", + description: "在3D空间中标注目标对象的位置和方向", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=3D+Object+Detection", + icon: , + }, + { + id: "cv-9", + name: "图像配对", + category: "Computer Vision", + description: "标注图像之间的对应关系", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Image+Matching", + icon: , + }, + { + id: "cv-10", + name: "图像质量评估", + category: "Computer Vision", + description: "评估和标注图像质量等级", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Quality+Assessment", + icon: , + }, + { + id: "cv-11", + name: "图像裁剪标注", + category: "Computer Vision", + description: "标注图像中需要裁剪的区域", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Image+Cropping", + icon: , + }, + { + id: "cv-12", + name: "图像旋转标注", + category: "Computer Vision", + description: "标注图像的正确方向角度", + type: "image", + preview: "/placeholder.svg?height=120&width=180&text=Image+Rotation", + icon: , + }, + // 自然语言处理模板 + { + id: "nlp-1", + name: "文本分类", + category: "Natural Language Processing", + description: "为文本分配类别标签", + type: "text", + icon: , + }, + { + id: "nlp-2", + name: "命名实体识别", + category: "Natural Language Processing", + description: "识别和标注文本中的实体", + type: "text", + icon: , + }, + { + id: "nlp-3", + name: "情感分析", + category: "Natural Language Processing", + description: "标注文本的情感倾向", + type: "text", + icon: , + }, + { + id: "nlp-4", + name: "问答标注", + category: "Natural Language Processing", + description: "标注问题和答案对", + type: "text", + icon: , + }, + { + id: "nlp-5", + name: "文本摘要", + category: "Natural Language Processing", + description: "为长文本创建摘要标注", + type: "text", + icon: , + }, + { + id: "nlp-6", + name: "对话标注", + category: "Natural Language Processing", + description: "标注对话中的意图和实体", + type: "text", + icon: , + }, + { + id: "nlp-7", + name: "关系抽取", + category: "Natural Language Processing", + description: "标注实体之间的关系", + type: "text", + icon: , + }, + { + id: "nlp-8", + name: "文本相似度", + category: "Natural Language Processing", + description: "标注文本之间的相似度", + type: "text", + icon: , + }, + { + id: "nlp-9", + name: "语言检测", + category: "Natural Language Processing", + description: "识别和标注文本的语言类型", + type: "text", + icon: , + }, + { + id: "nlp-10", + name: "文本纠错", + category: "Natural Language Processing", + description: "标注文本中的错误并提供修正", + type: "text", + icon: , + }, +] \ No newline at end of file diff --git a/frontend/src/mock/cleansing.tsx b/frontend/src/mock/cleansing.tsx new file mode 100644 index 0000000..9d90184 --- /dev/null +++ b/frontend/src/mock/cleansing.tsx @@ -0,0 +1,56 @@ +import { + DatabaseOutlined, + BarChartOutlined, + FileTextOutlined, + ThunderboltOutlined, + PictureOutlined, + CalculatorOutlined, + SwapOutlined, +} from "@ant-design/icons"; +import { FileImage, FileText, Music, Repeat, Video } from "lucide-react"; + +// 模板类型选项 +export const templateTypes = [ + { + value: "text", + label: "文本", + icon: FileText, + description: "处理文本数据的清洗模板", + }, + { + value: "image", + label: "图片", + icon: FileImage, + description: "处理图像数据的清洗模板", + }, + { + value: "video", + label: "视频", + icon: Video, + description: "处理视频数据的清洗模板", + }, + { + value: "audio", + label: "音频", + icon: Music, + description: "处理音频数据的清洗模板", + }, + { + value: "image-to-text", + label: "图片转文本", + icon: Repeat, + description: "图像识别转文本的处理模板", + }, +]; + +// 算子分类 +export const OPERATOR_CATEGORIES = { + data: { name: "数据清洗", icon: , color: "#1677ff" }, + ml: { name: "机器学习", icon: , color: "#722ed1" }, + vision: { name: "计算机视觉", icon: , color: "#52c41a" }, + nlp: { name: "自然语言处理", icon: , color: "#faad14" }, + analysis: { name: "数据分析", icon: , color: "#f5222d" }, + transform: { name: "数据转换", icon: , color: "#13c2c2" }, + io: { name: "输入输出", icon: , color: "#595959" }, + math: { name: "数学计算", icon: , color: "#fadb14" }, +}; diff --git a/frontend/src/mock/evaluation.tsx b/frontend/src/mock/evaluation.tsx new file mode 100644 index 0000000..5caad2d --- /dev/null +++ b/frontend/src/mock/evaluation.tsx @@ -0,0 +1,290 @@ +// 预设评估维度配置 +export const presetEvaluationDimensions: EvaluationDimension[] = [ + { + id: "answer_relevance", + name: "回答相关性", + description: "评估回答内容是否针对问题,是否切中要点", + category: "accuracy", + isEnabled: true, + }, + { + id: "content_quality", + name: "内容质量", + description: "评估内容的准确性、完整性和可读性", + category: "quality", + isEnabled: true, + }, + { + id: "information_completeness", + name: "信息完整性", + description: "评估信息是否完整,无缺失关键内容", + category: "completeness", + isEnabled: true, + }, + { + id: "language_fluency", + name: "语言流畅性", + description: "评估语言表达是否流畅自然", + category: "quality", + isEnabled: true, + }, + { + id: "factual_accuracy", + name: "事实准确性", + description: "评估内容中事实信息的准确性", + category: "accuracy", + isEnabled: true, + }, +] + + +export const sliceOperators: SliceOperator[] = [ + { + id: "paragraph-split", + name: "段落分割", + description: "按段落自然分割文档", + type: "text", + icon: "📄", + params: { minLength: 50, maxLength: 1000 }, + }, + { + id: "sentence-split", + name: "句子分割", + description: "按句子边界分割文档", + type: "text", + icon: "📝", + params: { maxSentences: 5, overlap: 1 }, + }, + { + id: "semantic-split", + name: "语义分割", + description: "基于语义相似度智能分割", + type: "semantic", + icon: "🧠", + params: { threshold: 0.7, windowSize: 3 }, + }, + { + id: "length-split", + name: "长度分割", + description: "按固定字符长度分割", + type: "text", + icon: "📏", + params: { chunkSize: 512, overlap: 50 }, + }, + { + id: "structure-split", + name: "结构化分割", + description: "按文档结构(标题、章节)分割", + type: "structure", + icon: "🏗️", + params: { preserveHeaders: true, minSectionLength: 100 }, + }, + { + id: "table-extract", + name: "表格提取", + description: "提取并单独处理表格内容", + type: "structure", + icon: "📊", + params: { includeHeaders: true, mergeRows: false }, + }, + { + id: "code-extract", + name: "代码提取", + description: "识别并提取代码块", + type: "custom", + icon: "💻", + params: { languages: ["python", "javascript", "sql"], preserveIndentation: true }, + }, + { + id: "qa-extract", + name: "问答提取", + description: "自动识别问答格式内容", + type: "semantic", + icon: "❓", + params: { confidenceThreshold: 0.8, generateAnswers: true }, + }, +] + + +export const mockTasks: EvaluationTask[] = [ + { + id: "1", + name: "客服对话数据质量评估", + datasetId: "1", + datasetName: "客服对话数据集", + evaluationType: "model", + status: "completed", + score: 85, + progress: 100, + createdAt: "2024-01-15 14:30", + completedAt: "2024-01-15 14:45", + description: "评估客服对话数据的质量,包括对话完整性、回复准确性等维度", + dimensions: ["answer_relevance", "content_quality", "information_completeness"], + customDimensions: [], + sliceConfig: { + threshold: 0.8, + sampleCount: 100, + method: "语义分割", + }, + modelConfig: { + url: "https://api.openai.com/v1/chat/completions", + apiKey: "sk-***", + prompt: "请从数据质量、标签准确性、标注一致性三个维度评估这个客服对话数据集...", + temperature: 0.3, + maxTokens: 2000, + }, + metrics: { + accuracy: 88, + completeness: 92, + consistency: 78, + relevance: 85, + }, + issues: [ + { type: "重复数据", count: 23, severity: "medium" }, + { type: "格式错误", count: 5, severity: "high" }, + { type: "内容不完整", count: 12, severity: "low" }, + ], + }, + { + id: "2", + name: "产品评论人工评估", + datasetId: "2", + datasetName: "产品评论数据集", + evaluationType: "manual", + status: "pending", + progress: 0, + createdAt: "2024-01-15 15:20", + description: "人工评估产品评论数据的情感标注准确性", + dimensions: ["content_quality", "factual_accuracy"], + customDimensions: [ + { + id: "custom_1", + name: "情感极性准确性", + description: "评估情感标注的极性(正面/负面/中性)准确性", + category: "custom", + isCustom: true, + isEnabled: true, + }, + ], + sliceConfig: { + threshold: 0.7, + sampleCount: 50, + method: "段落分割", + }, + metrics: { + accuracy: 0, + completeness: 0, + consistency: 0, + relevance: 0, + }, + issues: [], + }, + { + id: "3", + name: "新闻分类数据评估", + datasetId: "4", + datasetName: "新闻分类数据集", + evaluationType: "manual", + status: "running", + progress: 65, + createdAt: "2024-01-15 16:10", + description: "人工评估新闻分类数据集的标注质量", + dimensions: ["content_quality", "information_completeness", "factual_accuracy"], + customDimensions: [], + sliceConfig: { + threshold: 0.9, + sampleCount: 80, + method: "句子分割", + }, + metrics: { + accuracy: 82, + completeness: 78, + consistency: 85, + relevance: 80, + }, + issues: [{ type: "标注不一致", count: 15, severity: "medium" }], + }, +] + +// 模拟QA对数据 +export const mockQAPairs: QAPair[] = [ + { + id: "qa_1", + question: "这个产品的退货政策是什么?", + answer: "我们提供7天无理由退货服务,商品需要保持原包装完整。", + sliceId: "slice_1", + score: 4.5, + feedback: "回答准确且完整", + }, + { + id: "qa_2", + question: "如何联系客服?", + answer: "您可以通过在线客服、电话400-123-4567或邮箱service@company.com联系我们。", + sliceId: "slice_2", + score: 5.0, + feedback: "提供了多种联系方式,非常全面", + }, + { + id: "qa_3", + question: "配送时间需要多久?", + answer: "一般情况下,我们会在1-3个工作日内发货,配送时间根据地区不同为2-7天。", + sliceId: "slice_3", + score: 4.0, + feedback: "时间范围说明清楚", + }, +] +// 评估维度模板配置 +export const evaluationTemplates = { + dialogue_text: { + name: "对话文本评估", + dimensions: [ + { + id: "answer_relevance", + name: "回答是否有针对性", + description: "评估回答内容是否针对问题,是否切中要点", + category: "accuracy" as const, + isEnabled: true, + }, + { + id: "question_correctness", + name: "问题是否正确", + description: "评估问题表述是否清晰、准确、合理", + category: "quality" as const, + isEnabled: true, + }, + { + id: "answer_independence", + name: "回答是否独立", + description: "评估回答是否独立完整,不依赖外部信息", + category: "completeness" as const, + isEnabled: true, + }, + ], + }, + data_quality: { + name: "数据质量评估", + dimensions: [ + { + id: "data_quality", + name: "数据质量", + description: "评估数据的整体质量,包括格式规范性、完整性等", + category: "quality" as const, + isEnabled: true, + }, + { + id: "label_accuracy", + name: "标签准确性", + description: "评估数据标签的准确性和一致性", + category: "accuracy" as const, + isEnabled: true, + }, + { + id: "data_completeness", + name: "数据完整性", + description: "评估数据集的完整性,是否存在缺失数据", + category: "completeness" as const, + isEnabled: true, + }, + ], + }, +} \ No newline at end of file diff --git a/frontend/src/mock/knowledgeBase.tsx b/frontend/src/mock/knowledgeBase.tsx new file mode 100644 index 0000000..b2b9fb5 --- /dev/null +++ b/frontend/src/mock/knowledgeBase.tsx @@ -0,0 +1,254 @@ +export const mockChunks = Array.from({ length: 23 }, (_, i) => ({ + id: i + 1, + content: `这是第 ${ + i + 1 + } 个文档分块的内容示例。在实际应用中,这里会显示从原始文档中提取和分割的具体文本内容。用户可以在这里查看和编辑分块的内容,确保知识库的质量和准确性。这个分块包含了重要的业务信息和技术细节,需要仔细维护以确保检索的准确性。`, + position: i + 1, + tokens: Math.floor(Math.random() * 200) + 100, + embedding: Array.from({ length: 1536 }, () => Math.random() - 0.5), + similarity: (Math.random() * 0.3 + 0.7).toFixed(3), + createdAt: "2024-01-22 10:35", + updatedAt: "2024-01-22 10:35", + vectorId: `vec_${i + 1}_${Math.random().toString(36).substr(2, 9)}`, + sliceOperator: ["semantic-split", "paragraph-split", "table-extract"][ + Math.floor(Math.random() * 3) + ], + parentChunkId: i > 0 ? Math.floor(Math.random() * i) + 1 : undefined, + metadata: { + source: "API文档.pdf", + page: Math.floor(i / 5) + 1, + section: `第${Math.floor(i / 3) + 1}章`, + }, +})); + +export const mockQAPairs = [ + { + id: 1, + question: "什么是API文档的主要用途?", + answer: + "API文档的主要用途是为开发者提供详细的接口说明,包括请求参数、响应格式和使用示例.", + }, + { + id: 2, + question: "如何正确使用这个API?", + answer: + "使用API时需要先获取访问令牌,然后按照文档中的格式发送请求,注意处理错误响应.", + }, +]; + +export const sliceOperators: SliceOperator[] = [ + { + id: "paragraph-split", + name: "段落分割", + description: "按段落自然分割文档", + type: "text", + icon: "📄", + params: { minLength: 50, maxLength: 1000 }, + }, + { + id: "sentence-split", + name: "句子分割", + description: "按句子边界分割文档", + type: "text", + icon: "📝", + params: { maxSentences: 5, overlap: 1 }, + }, + { + id: "semantic-split", + name: "语义分割", + description: "基于语义相似度智能分割", + type: "semantic", + icon: "🧠", + params: { threshold: 0.7, windowSize: 3 }, + }, + { + id: "length-split", + name: "长度分割", + description: "按固定字符长度分割", + type: "text", + icon: "📏", + params: { chunkSize: 512, overlap: 50 }, + }, + { + id: "structure-split", + name: "结构化分割", + description: "按文档结构(标题、章节)分割", + type: "structure", + icon: "🏗️", + params: { preserveHeaders: true, minSectionLength: 100 }, + }, + { + id: "table-extract", + name: "表格提取", + description: "提取并单独处理表格内容", + type: "structure", + icon: "📊", + params: { includeHeaders: true, mergeRows: false }, + }, + { + id: "code-extract", + name: "代码提取", + description: "识别并提取代码块", + type: "custom", + icon: "💻", + params: { + languages: ["python", "javascript", "sql"], + preserveIndentation: true, + }, + }, + { + id: "qa-extract", + name: "问答提取", + description: "自动识别问答格式内容", + type: "semantic", + icon: "❓", + params: { confidenceThreshold: 0.8, generateAnswers: true }, + }, +]; + +export const vectorDatabases = [ + { + id: "pinecone", + name: "Pinecone", + description: "云端向量数据库,高性能检索", + }, + { + id: "weaviate", + name: "Weaviate", + description: "开源向量数据库,支持多模态", + }, + { id: "qdrant", name: "Qdrant", description: "高性能向量搜索引擎" }, + { id: "chroma", name: "ChromaDB", description: "轻量级向量数据库" }, + { id: "milvus", name: "Milvus", description: "分布式向量数据库" }, + { id: "faiss", name: "FAISS", description: "Facebook AI 相似性搜索库" }, +]; + +export const mockKnowledgeBases: KnowledgeBase[] = [ + { + id: 1, + name: "产品技术文档库", + description: + "包含所有产品相关的技术文档和API说明,支持多种格式文档的智能解析和向量化处理", + type: "unstructured", + status: "ready", + fileCount: 45, + chunkCount: 1250, + vectorCount: 1250, + size: "2.3 GB", + progress: 100, + createdAt: "2024-01-15", + lastUpdated: "2024-01-22", + vectorDatabase: "pinecone", + config: { + embeddingModel: "text-embedding-3-large", + llmModel: "gpt-4o", + chunkSize: 512, + overlap: 50, + sliceMethod: "semantic", + enableQA: true, + vectorDimension: 1536, + sliceOperators: ["semantic-split", "paragraph-split", "table-extract"], + }, + files: [ + { + id: 1, + name: "API文档.pdf", + type: "pdf", + size: "2.5 MB", + status: "completed", + chunkCount: 156, + progress: 100, + uploadedAt: "2024-01-15", + source: "upload", + vectorizationStatus: "completed", + }, + { + id: 2, + name: "用户手册.docx", + type: "docx", + size: "1.8 MB", + status: "disabled", + chunkCount: 89, + progress: 65, + uploadedAt: "2024-01-22", + source: "dataset", + datasetId: "dataset-1", + vectorizationStatus: "failed", + }, + ], + vectorizationHistory: [ + { + id: 1, + timestamp: "2024-01-22 14:30:00", + operation: "create", + fileId: 1, + fileName: "API文档.pdf", + chunksProcessed: 156, + vectorsGenerated: 156, + status: "success", + duration: "2m 15s", + config: { + embeddingModel: "text-embedding-3-large", + chunkSize: 512, + sliceMethod: "semantic", + }, + }, + { + id: 2, + timestamp: "2024-01-22 15:45:00", + operation: "update", + fileId: 2, + fileName: "用户手册.docx", + chunksProcessed: 89, + vectorsGenerated: 0, + status: "failed", + duration: "0m 45s", + config: { + embeddingModel: "text-embedding-3-large", + chunkSize: 512, + sliceMethod: "semantic", + }, + error: "向量化服务连接超时", + }, + ], + }, + { + id: 2, + name: "FAQ结构化知识库", + description: "客服常见问题的结构化问答对,支持快速检索和智能匹配", + type: "structured", + status: "vectorizing", + fileCount: 12, + chunkCount: 890, + vectorCount: 750, + size: "156 MB", + progress: 75, + createdAt: "2024-01-20", + lastUpdated: "2024-01-23", + vectorDatabase: "weaviate", + config: { + embeddingModel: "text-embedding-ada-002", + chunkSize: 256, + overlap: 0, + sliceMethod: "paragraph", + enableQA: false, + vectorDimension: 1536, + sliceOperators: ["qa-extract", "paragraph-split"], + }, + files: [ + { + id: 3, + name: "FAQ模板.xlsx", + type: "xlsx", + size: "450 KB", + status: "vectorizing", + chunkCount: 234, + progress: 75, + uploadedAt: "2024-01-20", + source: "upload", + vectorizationStatus: "processing", + }, + ], + vectorizationHistory: [], + }, +]; diff --git a/frontend/src/mock/mock-apis.cjs b/frontend/src/mock/mock-apis.cjs new file mode 100644 index 0000000..ae601e6 --- /dev/null +++ b/frontend/src/mock/mock-apis.cjs @@ -0,0 +1,149 @@ +const { addMockPrefix } = require("./mock-core/util.cjs"); + +const MockAPI = { + // 数据归集接口 + queryTasksUsingPost: "/data-collection/tasks", // 获取数据源任务列表 + createTaskUsingPost: "/data-collection/tasks/create", // 创建数据源任务 + queryTaskByIdUsingGet: "/data-collection/tasks/:id", // 根据ID获取数据源任务详情 + updateTaskByIdUsingPut: "/data-collection/tasks/:id", // 更新数据源任务 + deleteTaskByIdUsingDelete: "/data-collection/tasks/:id", // 删除数据源任务 + executeTaskByIdUsingPost: "/data-collection/tasks/:id/execute", // 执行数据源任务 + stopTaskByIdUsingPost: "/data-collection/tasks/:id/stop", // 停止数据源任务 + queryExecutionLogUsingPost: "/data-collection/executions", // 获取任务执行日志 + queryExecutionLogByIdUsingGet: "/data-collection/executions/:id", // 获取任务执行日志详情 + queryCollectionStatisticsUsingGet: "/data-collection/monitor/statistics", // 获取数据归集统计信息 + + // 数据管理接口 + queryDatasetsUsingGet: "/data-management/datasets", // 获取数据集列表 + createDatasetUsingPost: "/data-management/datasets", // 创建数据集 + queryDatasetByIdUsingGet: "/data-management/datasets/:id", // 根据ID获取数据集详情 + updateDatasetByIdUsingPut: "/data-management/datasets/:id", // 更新数据集 + deleteDatasetByIdUsingDelete: "/data-management/datasets/:id", // 删除数据集 + queryFilesUsingGet: "/data-management/datasets/:id/files", // 获取数据集文件列表 + uploadFileUsingPost: "/data-management/datasets/:id/files", // 添加数据集文件 + queryFileByIdUsingGet: "/data-management/datasets/:id/files/:fileId", // 获取数据集文件详情 + deleteFileByIdUsingDelete: "/data-management/datasets/:id/files/:fileId", // 删除数据集文件 + downloadFileByIdUsingGet: + "/data-management/datasets/:id/files/:fileId/download", // 下载文件 + queryDatasetTypesUsingGet: "/data-management/dataset-types", // 获取数据集类型列表 + queryTagsUsingGet: "/data-management/tags", // 获取数据集标签列表 + createTagUsingPost: "/data-management/tags", // 创建数据集标签 + updateTagUsingPost: "/data-management/tags", // 更新数据集标签 + deleteTagUsingPost: "/data-management/tags", // 删除数据集标签 + queryDatasetStatisticsUsingGet: "/data-management/datasets/statistics", // 获取数据集统计信息 + preUploadFileUsingPost: "/data-management/datasets/:id/upload/pre-upload", // 预上传文件 + cancelUploadUsingPut: "/data-management/datasets/upload/cancel-upload/:id", // 取消上传 + uploadFileChunkUsingPost: "/data-management/datasets/:id/upload/chunk", // 上传切片 + + // 数据清洗接口 + queryCleaningTasksUsingGet: "/cleaning/tasks", // 获取清洗任务列表 + createCleaningTaskUsingPost: "/cleaning/tasks", // 创建清洗任务 + queryCleaningTaskByIdUsingGet: "/cleaning/tasks/:taskId", // 根据ID获取清洗任务详情 + deleteCleaningTaskByIdUsingDelete: "/cleaning/tasks/:taskId", // 删除清洗任务 + executeCleaningTaskUsingPost: "/cleaning/tasks/:taskId/execute", // 执行清洗任务 + stopCleaningTaskUsingPost: "/cleaning/tasks/:taskId/stop", // 停止清洗任务 + queryCleaningTemplatesUsingGet: "/cleaning/templates", // 获取清洗模板列表 + createCleaningTemplateUsingPost: "/cleaning/templates", // 创建清洗模板 + queryCleaningTemplateByIdUsingGet: "/cleaning/templates/:templateId", // 根据ID获取清洗模板详情 + updateCleaningTemplateByIdUsingPut: "/cleaning/templates/:templateId", // 根据ID更新清洗模板详情 + deleteCleaningTemplateByIdUsingDelete: "/cleaning/templates/:templateId", // 删除清洗模板 + + // 数据标注接口 + queryAnnotationTasksUsingGet: "/project/mappings/list", // 获取标注任务列表 + createAnnotationTaskUsingPost: "/project/create", // 创建标注任务 + syncAnnotationTaskByIdUsingPost: "/project/sync", // 同步标注任务 + deleteAnnotationTaskByIdUsingDelete: "/project/mappings", // 删除标注任务 + queryAnnotationTaskByIdUsingGet: "/annotation/tasks/:taskId", // 根据ID获取标注任务详情 + executeAnnotationTaskByIdUsingPost: "/annotation/tasks/:taskId/execute", // 执行标注任务 + stopAnnotationTaskByIdUsingPost: "/annotation/tasks/:taskId/stop", // 停止标注任务 + queryAnnotationDataUsingGet: "/annotation/data", // 获取标注数据列表 + submitAnnotationUsingPost: "/annotation/submit/:id", // 提交标注 + updateAnnotationUsingPut: "/annotation/update/:id", // 根据ID更新标注 + deleteAnnotationUsingDelete: "/annotation/delete/:id", // 根据ID删除标注 + startAnnotationTaskUsingPost: "/annotation/start/:taskId", // 开始标注任务 + pauseAnnotationTaskUsingPost: "/annotation/pause/:taskId", // 暂停标注任务 + resumeAnnotationTaskUsingPost: "/annotation/resume/:taskId", // 恢复标注任务 + completeAnnotationTaskUsingPost: "/annotation/complete/:taskId", // 完成标注任务 + getAnnotationTaskStatisticsUsingGet: "/annotation/statistics/:taskId", // 获取标注任务统计信息 + getAnnotationStatisticsUsingGet: "/annotation/statistics", // 获取标注统计信息 + queryAnnotationTemplatesUsingGet: "/annotation/templates", // 获取标注模板列表 + createAnnotationTemplateUsingPost: "/annotation/templates", // 创建标注模板 + queryAnnotationTemplateByIdUsingGet: "/annotation/templates/:templateId", // 根据ID获取标注模板详情 + queryAnnotatorsUsingGet: "/annotation/annotators", // 获取标注者列表 + assignAnnotatorUsingPost: "/annotation/annotators/:annotatorId", // 分配标注者 + + // 数据合成接口 + querySynthesisJobsUsingGet: "/synthesis/jobs", // 获取合成任务列表 + createSynthesisJobUsingPost: "/synthesis/jobs/create", // 创建合成任务 + querySynthesisJobByIdUsingGet: "/synthesis/jobs/:jobId", // 根据ID获取合成任务详情 + updateSynthesisJobByIdUsingPut: "/synthesis/jobs/:jobId", // 更新合成任务 + deleteSynthesisJobByIdUsingDelete: "/synthesis/jobs/:jobId", // 删除合成任务 + executeSynthesisJobUsingPost: "/synthesis/jobs/execute/:jobId", // 执行合成任务 + stopSynthesisJobByIdUsingPost: "/synthesis/jobs/stop/:jobId", // 停止合成任务 + querySynthesisTemplatesUsingGet: "/synthesis/templates", // 获取合成模板列表 + createSynthesisTemplateUsingPost: "/synthesis/templates/create", // 创建合成模板 + querySynthesisTemplateByIdUsingGet: "/synthesis/templates/:templateId", // 根据ID获取合成模板详情 + updateSynthesisTemplateByIdUsingPut: "/synthesis/templates/:templateId", // 更新合成模板 + deleteSynthesisTemplateByIdUsingDelete: "/synthesis/templates/:templateId", // 删除合成模板 + queryInstructionTemplatesUsingPost: "/synthesis/templates", // 获取指令模板列表 + createInstructionTemplateUsingPost: "/synthesis/templates/create", // 创建指令模板 + queryInstructionTemplateByIdUsingGet: "/synthesis/templates/:templateId", // 根据ID获取指令模板详情 + deleteInstructionTemplateByIdUsingDelete: "/synthesis/templates/:templateId", // 删除指令模板 + instructionTuningUsingPost: "/synthesis/instruction-tuning", // 指令微调 + cotDistillationUsingPost: "/synthesis/cot-distillation", // Cot蒸馏 + queryOperatorsUsingPost: "/synthesis/operators", // 获取操作列表 + + // 数据评测接口 + queryEvaluationTasksUsingPost: "/evaluation/tasks", // 获取评测任务列表 + createEvaluationTaskUsingPost: "/evaluation/tasks/create", // 创建评测任务 + queryEvaluationTaskByIdUsingGet: "/evaluation/tasks/:taskId", // 根据ID获取评测任务详情 + updateEvaluationTaskByIdUsingPut: "/evaluation/tasks/:taskId", // 更新评测任务 + deleteEvaluationTaskByIdUsingDelete: "/evaluation/tasks/:taskId", // 删除评测任务 + executeEvaluationTaskByIdUsingPost: "/evaluation/tasks/:taskId/execute", // 执行评测任务 + stopEvaluationTaskByIdUsingPost: "/evaluation/tasks/:taskId/stop", // 停止评测任务 + queryEvaluationReportsUsingPost: "/evaluation/reports", // 获取评测报告列表 + queryEvaluationReportByIdUsingGet: "/evaluation/reports/:reportId", // 根据ID获取评测报告详情 + manualEvaluateUsingPost: "/evaluation/manual-evaluate", // 人工评测 + queryEvaluationStatisticsUsingGet: "/evaluation/statistics", // 获取评测统计信息 + evaluateDataQualityUsingPost: "/evaluation/data-quality", // 数据质量评测 + getQualityEvaluationByIdUsingGet: "/evaluation/data-quality/:id", // 根据ID获取数据质量评测详情 + evaluateCompatibilityUsingPost: "/evaluation/compatibility", // 兼容性评测 + evaluateValueUsingPost: "/evaluation/value", // 价值评测 + queryEvaluationReportsUsingGet: "/evaluation/reports", // 获取评测报告列表(简化版) + getEvaluationReportByIdUsingGet: "/evaluation/reports/:reportId", // 根据ID获取评测报告详情(简化版) + exportEvaluationReportUsingGet: "/evaluation/reports/:reportId/export", // 导出评测报告 + batchEvaluationUsingPost: "/evaluation/batch-evaluate", // 批量评测 + + // 知识生成接口 + queryKnowledgeBasesUsingPost: "/knowledge/bases", // 获取知识库列表 + createKnowledgeBaseUsingPost: "/knowledge/bases/create", // 创建知识库 + queryKnowledgeBaseByIdUsingGet: "/knowledge/bases/:baseId", // 根据ID获取知识库详情 + updateKnowledgeBaseByIdUsingPut: "/knowledge/bases/:baseId", // 更新知识库 + deleteKnowledgeBaseByIdUsingDelete: "/knowledge/bases/:baseId", // 删除知识库 + queryKnowledgeGenerationTasksUsingPost: "/knowledge/tasks", // 获取知识生成任务列表 + createKnowledgeGenerationTaskUsingPost: "/knowledge/tasks/create", // 创建知识生成任务 + queryKnowledgeGenerationTaskByIdUsingGet: "/knowledge/tasks/:taskId", // 根据ID获取知识生成任务详情 + updateKnowledgeGenerationTaskByIdUsingPut: "/knowledge/tasks/:taskId", // 更新知识生成任务 + deleteKnowledgeGenerationTaskByIdUsingDelete: "/knowledge/tasks/:taskId", // 删除知识生成任务 + executeKnowledgeGenerationTaskByIdUsingPost: + "/knowledge/tasks/:taskId/execute", // 执行知识生成任务 + stopKnowledgeGenerationTaskByIdUsingPost: "/knowledge/tasks/:taskId/stop", // 停止知识生成任务 + queryKnowledgeStatisticsUsingGet: "/knowledge/statistics", // 获取知识生成 + + // 算子市场 + queryOperatorsUsingPost: "/operators/list", // 获取算子列表 + queryCategoryTreeUsingGet: "/categories/tree", // 获取算子分类树 + queryOperatorByIdUsingGet: "/operators/:operatorId", // 根据ID获取算子详情 + createOperatorUsingPost: "/operators/create", // 创建算子 + updateOperatorByIdUsingPut: "/operators/:operatorId", // 更新算子 + uploadOperatorUsingPost: "/operators/upload", // 上传算子 + createLabelUsingPost: "/operators/labels", // 创建算子标签 + queryLabelsUsingGet: "/labels", // 获取算子标签列表 + deleteLabelsUsingDelete: "/labels", // 删除算子标签 + updateLabelByIdUsingPut: "/labels/:labelId", // 更新算子标签 + deleteOperatorByIdUsingDelete: "/operators/:operatorId", // 删除算子 + publishOperatorUsingPost: "/operators/:operatorId/publish", // 发布算子 + unpublishOperatorUsingPost: "/operators/:operatorId/unpublish", // 下架算子 +}; + +module.exports = addMockPrefix("/api", MockAPI); diff --git a/frontend/src/mock/mock-core/module-loader.cjs b/frontend/src/mock/mock-core/module-loader.cjs new file mode 100644 index 0000000..4084fc1 --- /dev/null +++ b/frontend/src/mock/mock-core/module-loader.cjs @@ -0,0 +1,25 @@ +const fs = require('fs'); + +function loadAllMockModules(router, pathDir) { + if (!fs.existsSync(pathDir)) { + throw new Error(`Mock directory ${pathDir} does not exist.`); + } + + const files = fs.readdirSync(pathDir); + files.forEach(file => { + const filePath = `${pathDir}/${file}`; + if(fs.lstatSync(filePath).isDirectory()) { + loadAllMockModules(router, filePath); + } else { + let fileNameModule = file.replace('/\.js\b$/', ''); + let module = require(`${pathDir}/${fileNameModule}`); + if(typeof module === 'function' && module.length === 1) { + module(router); + } + } + }); +} + +module.exports = { + loadAllMockModules, +}; \ No newline at end of file diff --git a/frontend/src/mock/mock-core/session-helper.cjs b/frontend/src/mock/mock-core/session-helper.cjs new file mode 100644 index 0000000..d92f382 --- /dev/null +++ b/frontend/src/mock/mock-core/session-helper.cjs @@ -0,0 +1,63 @@ +const path = require("path"); +const Mock = require("mockjs"); +const session = require("express-session"); +const FileStore = require("session-file-store")(session); + +const { isFunction } = require("lodash"); + +const argv = require("minimist")(process.argv.slice(2)); +const isDev = (argv.env || "development") === "development"; +const TOKEN_KEY = isDev ? "X-Auth-Token" : "X-Csrf-Token"; + +const setSessionUser = (req, getLoginInfo) => { + if (!isFunction(getLoginInfo)) { + throw new Error("getLoginInfo must be a function"); + } + + if (!req.session?.users) { + req.session.users = {}; + } + + let token = req.get(TOKEN_KEY); + const { users } = req.session; + if (!token || !users[token]) { + token = Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""); + const userInfo = getLoginInfo(req) || {}; + users[token] = user; + } + return token; +}; + +const getSessionUser = (req) => { + const token = req.get(TOKEN_KEY); + if (token && req.session?.users) { + return req.session.users[token]; + } + return null; +}; + +const genExpressSession = () => { + return session({ + name: "demo.name", + secret: "demo.secret", + resave: true, + saveUninitialized: true, + cookie: { + maxAge: 60 * 60 * 1e3, + expires: new Date(Date.now() + 60 * 60 * 1e3), + }, // 1 hour + store: new FileStore({ + path: path.join(__dirname, "../sessions"), + retries: 0, + keyFunction: (secret, sessionId) => { + return secret + sessionId; + }, + }), + }); +}; + +module.exports = { + setSessionUser, + getSessionUser, + genExpressSession, +}; diff --git a/frontend/src/mock/mock-core/util.cjs b/frontend/src/mock/mock-core/util.cjs new file mode 100644 index 0000000..ef4a065 --- /dev/null +++ b/frontend/src/mock/mock-core/util.cjs @@ -0,0 +1,30 @@ + +function log(message, type = "log", provided = 'console') { + const providedFn = globalThis[provided] || console; + if (providedFn && typeof providedFn[type] === 'function') { + const invokeMethod = providedFn[type ?? 'log']; + invokeMethod.call(providedFn, message); + } +} + +function addMockPrefix(urlPrefix, api) { + const newMockApi = {}; + Object.keys(api).map(apiKey=>{ + newMockApi[apiKey] = urlPrefix + api[apiKey]; + }); + + return new Proxy(newMockApi, { + get(target, prop) { + if (prop in target) { + return target[prop]; + } else { + throw new Error(`API ${String(prop)} is not defined.`); + } + } + }) +} + +module.exports = { + log, + addMockPrefix, +}; \ No newline at end of file diff --git a/frontend/src/mock/mock-middleware/error-handle-middleware.cjs b/frontend/src/mock/mock-middleware/error-handle-middleware.cjs new file mode 100644 index 0000000..d1e50c8 --- /dev/null +++ b/frontend/src/mock/mock-middleware/error-handle-middleware.cjs @@ -0,0 +1,13 @@ +const errorHandle = (err, req, res, next) => { + if(res.headersSent) { + return next(err); + } + console.error('Server Error:', err.message); + res.status(500).json({ + code: '500', + msg: 'Internal Server Error', + data: null, + }); +}; + +module.exports = errorHandle; diff --git a/frontend/src/mock/mock-middleware/index.cjs b/frontend/src/mock/mock-middleware/index.cjs new file mode 100644 index 0000000..01c9e56 --- /dev/null +++ b/frontend/src/mock/mock-middleware/index.cjs @@ -0,0 +1,11 @@ +const setHeader = require('./set-header-middleware.cjs'); +const strongMatch = require('./strong-match-middleware.cjs'); +const sendJSON = require('./send-json-middleawre.cjs'); +const errorHandle = require('./error-handle-middleware.cjs'); + +module.exports = { + setHeader, + strongMatch, + sendJSON, + errorHandle, +}; \ No newline at end of file diff --git a/frontend/src/mock/mock-middleware/send-json-middleawre.cjs b/frontend/src/mock/mock-middleware/send-json-middleawre.cjs new file mode 100644 index 0000000..b6a1a47 --- /dev/null +++ b/frontend/src/mock/mock-middleware/send-json-middleawre.cjs @@ -0,0 +1,18 @@ +const sendJSON = (req, res, next) => { + res.sendJSON = ( + data = null, + { code = '0', msg = 'success', statusCode = 200, timeout = 0 } = {} + ) => { + const timer = setTimeout(() => { + res.status(statusCode).json({ + code, + msg, + data, + }); + clearTimeout(timer); + }, timeout); + }; + next(); +}; + +module.exports = sendJSON; \ No newline at end of file diff --git a/frontend/src/mock/mock-middleware/set-header-middleware.cjs b/frontend/src/mock/mock-middleware/set-header-middleware.cjs new file mode 100644 index 0000000..821c6c3 --- /dev/null +++ b/frontend/src/mock/mock-middleware/set-header-middleware.cjs @@ -0,0 +1,14 @@ +const setHeader = (req, res, next) => { + res.set({ + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + 'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src *; font-src 'self';", + 'X-Content-Type-Options': 'nosniff', + 'X-Frame-Options': 'SAMEORIGIN', + 'X-XSS-Protection': '1; mode=block', + }); + next(); +}; + +module.exports = setHeader; \ No newline at end of file diff --git a/frontend/src/mock/mock-middleware/strong-match-middleware.cjs b/frontend/src/mock/mock-middleware/strong-match-middleware.cjs new file mode 100644 index 0000000..a11813e --- /dev/null +++ b/frontend/src/mock/mock-middleware/strong-match-middleware.cjs @@ -0,0 +1,13 @@ +const API = require('../mock-apis.cjs'); + +const strongMatch = (req, res, next) => { + res.strongMatch = () => { + const { url } = req; + const index = url.indexOf('?'); + const targetUrl = index !== -1 ? url.substring(0, index) : url; + const isExistedUrl = Object.values(API).includes(targetUrl); + return isExistedUrl; + }; + next(); +}; +module.exports = strongMatch; \ No newline at end of file diff --git a/frontend/src/mock/mock-seed/data-annotation.cjs b/frontend/src/mock/mock-seed/data-annotation.cjs new file mode 100644 index 0000000..6ba1799 --- /dev/null +++ b/frontend/src/mock/mock-seed/data-annotation.cjs @@ -0,0 +1,618 @@ +const Mock = require("mockjs"); +const API = require("../mock-apis.cjs"); + +// 标注任务数据 +function annotationTaskItem() { + return { + source_dataset_id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + mapping_id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + labelling_project_id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + labelling_project_name: Mock.Random.ctitle(5, 20), + created_at: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + last_updated_at: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + deleted_at: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + // id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + // name: Mock.Random.ctitle(5, 20), + // description: Mock.Random.csentence(5, 30), + // type: Mock.Random.pick([ + // "TEXT_CLASSIFICATION", + // "NAMED_ENTITY_RECOGNITION", + // "OBJECT_DETECTION", + // "SEMANTIC_SEGMENTATION", + // ]), + // status: Mock.Random.pick(["PENDING", "IN_PROGRESS", "COMPLETED", "PAUSED"]), + // datasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + // progress: Mock.Random.float(0, 100, 2, 2), + // createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + // updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + // createdBy: Mock.Random.cname(), + // assignedTo: Mock.Random.cname(), + // totalDataCount: Mock.Random.integer(100, 10000), + // annotatedCount: Mock.Random.integer(10, 500), + // configuration: { + // labels: Mock.Random.shuffle([ + // "正面", + // "负面", + // "中性", + // "人物", + // "地点", + // "组织", + // "时间", + // ]).slice(0, Mock.Random.integer(3, 5)), + // guidelines: Mock.Random.csentence(10, 50), + // qualityThreshold: Mock.Random.float(0.8, 1.0, 2, 2), + // }, + // statistics: { + // accuracy: Mock.Random.float(0.85, 0.99, 2, 2), + // averageTime: Mock.Random.integer(30, 300), // seconds + // reviewCount: Mock.Random.integer(0, 50), + // }, + }; +} + +const annotationTaskList = new Array(25).fill(null).map(annotationTaskItem); + +// 标注数据项 +function annotationDataItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + taskId: Mock.Random.pick(annotationTaskList).id, + content: Mock.Random.cparagraph(1, 3), + originalData: { + text: Mock.Random.cparagraph(1, 3), + source: Mock.Random.url(), + metadata: { + author: Mock.Random.cname(), + timestamp: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + }, + }, + annotations: [ + { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + label: Mock.Random.pick(["正面", "负面", "中性"]), + confidence: Mock.Random.float(0.7, 1.0, 2, 2), + annotator: Mock.Random.cname(), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + isPreAnnotation: Mock.Random.boolean(), + }, + ], + status: Mock.Random.pick(["PENDING", "ANNOTATED", "REVIEWED", "REJECTED"]), + priority: Mock.Random.integer(1, 5), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + }; +} + +const annotationDataList = new Array(200).fill(null).map(annotationDataItem); + +// 标注模板数据 +function annotationTemplateItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(5, 15), + description: Mock.Random.csentence(5, 25), + type: Mock.Random.pick([ + "TEXT_CLASSIFICATION", + "NAMED_ENTITY_RECOGNITION", + "OBJECT_DETECTION", + "SEMANTIC_SEGMENTATION", + ]), + category: Mock.Random.ctitle(3, 8), + labels: Mock.Random.shuffle([ + "正面", + "负面", + "中性", + "人物", + "地点", + "组织", + "时间", + "产品", + "服务", + ]).slice(0, Mock.Random.integer(3, 6)), + guidelines: Mock.Random.csentence(10, 50), + usageCount: Mock.Random.integer(0, 100), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + createdBy: Mock.Random.cname(), + }; +} + +const annotationTemplateList = new Array(15) + .fill(null) + .map(annotationTemplateItem); + +// 标注者数据 +function annotatorItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.cname(), + email: Mock.Random.email(), + role: Mock.Random.pick(["ANNOTATOR", "REVIEWER", "ADMIN"]), + skillLevel: Mock.Random.pick(["BEGINNER", "INTERMEDIATE", "EXPERT"]), + specialties: Mock.Random.shuffle([ + "文本分类", + "命名实体识别", + "目标检测", + "语义分割", + ]).slice(0, Mock.Random.integer(1, 3)), + statistics: { + totalAnnotations: Mock.Random.integer(100, 5000), + accuracy: Mock.Random.float(0.85, 0.99, 2, 2), + averageSpeed: Mock.Random.integer(50, 200), // annotations per hour + totalWorkTime: Mock.Random.integer(10, 500), // hours + }, + status: Mock.Random.pick(["ACTIVE", "INACTIVE", "SUSPENDED"]), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + }; +} + +const annotatorList = new Array(20).fill(null).map(annotatorItem); + +module.exports = function (router) { + // 获取标注任务列表 + router.get(API.queryAnnotationTasksUsingGet, (req, res) => { + const { page = 0, size = 20, status, type } = req.query; + let filteredTasks = annotationTaskList; + + if (status) { + filteredTasks = filteredTasks.filter((task) => task.status === status); + } + + if (type) { + filteredTasks = filteredTasks.filter((task) => task.type === type); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredTasks.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredTasks.length, + totalPages: Math.ceil(filteredTasks.length / size), + size: parseInt(size), + number: parseInt(page), + first: page == 0, + last: page >= Math.ceil(filteredTasks.length / size) - 1, + }, + }); + }); + + // 创建标注任务 + router.post(API.createAnnotationTaskUsingPost, (req, res) => { + const newTask = { + ...annotationTaskItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + status: "PENDING", + progress: 0, + annotatedCount: 0, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + annotationTaskList.push(newTask); + + res.status(201).send({ + code: "0", + msg: "Annotation task created successfully", + data: newTask, + }); + }); + + // 获取标注任务详情 + router.get(API.queryAnnotationTaskByIdUsingGet, (req, res) => { + const { taskId } = req.params; + const task = annotationTaskList.find((t) => t.id === taskId); + + if (task) { + res.send({ + code: "0", + msg: "Success", + data: task, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 更新标注任务 + router.put(API.syncAnnotationTaskByIdUsingPost, (req, res) => { + const { taskId } = req.params; + const index = annotationTaskList.findIndex((t) => t.id === taskId); + + if (index !== -1) { + annotationTaskList[index] = { + ...annotationTaskList[index], + ...req.body, + updatedAt: new Date().toISOString(), + }; + res.send({ + code: "0", + msg: "Annotation task updated successfully", + data: annotationTaskList[index], + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 删除标注任务 + router.delete(API.deleteAnnotationTaskByIdUsingDelete, (req, res) => { + const { taskId } = req.params; + const index = annotationTaskList.findIndex((t) => t.id === taskId); + + if (index !== -1) { + annotationTaskList.splice(index, 1); + res.send({ + code: "0", + msg: "Annotation task deleted successfully", + data: null, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 获取标注数据列表 + router.get(API.queryAnnotationDataUsingGet, (req, res) => { + const { taskId } = req.params; + const { page = 0, size = 20, status } = req.query; + + let filteredData = annotationDataList.filter( + (data) => data.taskId === taskId + ); + + if (status) { + filteredData = filteredData.filter((data) => data.status === status); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredData.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredData.length, + totalPages: Math.ceil(filteredData.length / size), + size: parseInt(size), + number: parseInt(page), + }, + }); + }); + + // 提交标注 + router.post(API.submitAnnotationUsingPost, (req, res) => { + const { taskId } = req.params; + const newAnnotation = { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + taskId, + ...req.body, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + res.status(201).send({ + code: "0", + msg: "Annotation submitted successfully", + data: newAnnotation, + }); + }); + + // 更新标注 + router.put(API.updateAnnotationUsingPut, (req, res) => { + const { taskId, annotationId } = req.params; + + res.send({ + code: "0", + msg: "Annotation updated successfully", + data: { + id: annotationId, + taskId, + ...req.body, + updatedAt: new Date().toISOString(), + }, + }); + }); + + // 删除标注 + router.delete(API.deleteAnnotationUsingDelete, (req, res) => { + const { taskId, annotationId } = req.params; + + res.send({ + code: "0", + msg: "Annotation deleted successfully", + data: null, + }); + }); + + // 开始标注任务 + router.post(API.startAnnotationTaskUsingPost, (req, res) => { + const { taskId } = req.params; + const task = annotationTaskList.find((t) => t.id === taskId); + + if (task) { + task.status = "IN_PROGRESS"; + task.updatedAt = new Date().toISOString(); + + res.send({ + code: "0", + msg: "Annotation task started successfully", + data: task, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 暂停标注任务 + router.post(API.pauseAnnotationTaskUsingPost, (req, res) => { + const { taskId } = req.params; + const task = annotationTaskList.find((t) => t.id === taskId); + + if (task) { + task.status = "PAUSED"; + task.updatedAt = new Date().toISOString(); + + res.send({ + code: "0", + msg: "Annotation task paused successfully", + data: task, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 恢复标注任务 + router.post(API.resumeAnnotationTaskUsingPost, (req, res) => { + const { taskId } = req.params; + const task = annotationTaskList.find((t) => t.id === taskId); + + if (task) { + task.status = "IN_PROGRESS"; + task.updatedAt = new Date().toISOString(); + + res.send({ + code: "0", + msg: "Annotation task resumed successfully", + data: task, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 完成标注任务 + router.post(API.completeAnnotationTaskUsingPost, (req, res) => { + const { taskId } = req.params; + const task = annotationTaskList.find((t) => t.id === taskId); + + if (task) { + task.status = "COMPLETED"; + task.progress = 100; + task.updatedAt = new Date().toISOString(); + + res.send({ + code: "0", + msg: "Annotation task completed successfully", + data: task, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 获取标注任务统计信息 + router.get(API.getAnnotationTaskStatisticsUsingGet, (req, res) => { + const { taskId } = req.params; + const task = annotationTaskList.find((t) => t.id === taskId); + + if (task) { + const statistics = { + taskId, + totalDataCount: task.totalDataCount, + annotatedCount: task.annotatedCount, + progress: task.progress, + accuracy: task.statistics.accuracy, + averageAnnotationTime: task.statistics.averageTime, + reviewCount: task.statistics.reviewCount, + qualityScore: Mock.Random.float(0.8, 0.99, 2, 2), + annotatorDistribution: { + [Mock.Random.cname()]: Mock.Random.integer(10, 100), + [Mock.Random.cname()]: Mock.Random.integer(10, 100), + [Mock.Random.cname()]: Mock.Random.integer(10, 100), + }, + }; + + res.send({ + code: "0", + msg: "Success", + data: statistics, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation task not found", + data: null, + }); + } + }); + + // 获取整体标注统计信息 + router.get(API.getAnnotationStatisticsUsingGet, (req, res) => { + const statistics = { + totalTasks: annotationTaskList.length, + completedTasks: annotationTaskList.filter((t) => t.status === "COMPLETED") + .length, + inProgressTasks: annotationTaskList.filter( + (t) => t.status === "IN_PROGRESS" + ).length, + pendingTasks: annotationTaskList.filter((t) => t.status === "PENDING") + .length, + totalAnnotations: annotationDataList.length, + totalAnnotators: annotatorList.length, + averageAccuracy: Mock.Random.float(0.85, 0.95, 2, 2), + taskTypeDistribution: { + TEXT_CLASSIFICATION: Mock.Random.integer(5, 15), + NAMED_ENTITY_RECOGNITION: Mock.Random.integer(3, 10), + OBJECT_DETECTION: Mock.Random.integer(2, 8), + SEMANTIC_SEGMENTATION: Mock.Random.integer(1, 5), + }, + }; + + res.send({ + code: "0", + msg: "Success", + data: statistics, + }); + }); + + // 获取标注模板列表 + router.get(API.queryAnnotationTemplatesUsingGet, (req, res) => { + const { page = 0, size = 20, type } = req.query; + let filteredTemplates = annotationTemplateList; + + if (type) { + filteredTemplates = filteredTemplates.filter( + (template) => template.type === type + ); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredTemplates.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredTemplates.length, + totalPages: Math.ceil(filteredTemplates.length / size), + size: parseInt(size), + number: parseInt(page), + }, + }); + }); + + // 创建标注模板 + router.post(API.createAnnotationTemplateUsingPost, (req, res) => { + const newTemplate = { + ...annotationTemplateItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + usageCount: 0, + createdAt: new Date().toISOString(), + }; + annotationTemplateList.push(newTemplate); + + res.status(201).send({ + code: "0", + msg: "Annotation template created successfully", + data: newTemplate, + }); + }); + + // 获取标注模板详情 + router.get(API.queryAnnotationTemplateByIdUsingGet, (req, res) => { + const { templateId } = req.params; + const template = annotationTemplateList.find((t) => t.id === templateId); + + if (template) { + res.send({ + code: "0", + msg: "Success", + data: template, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Annotation template not found", + data: null, + }); + } + }); + + // 获取标注者列表 + router.get(API.queryAnnotatorsUsingGet, (req, res) => { + const { page = 0, size = 20, status, skillLevel } = req.query; + let filteredAnnotators = annotatorList; + + if (status) { + filteredAnnotators = filteredAnnotators.filter( + (annotator) => annotator.status === status + ); + } + + if (skillLevel) { + filteredAnnotators = filteredAnnotators.filter( + (annotator) => annotator.skillLevel === skillLevel + ); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredAnnotators.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredAnnotators.length, + totalPages: Math.ceil(filteredAnnotators.length / size), + size: parseInt(size), + number: parseInt(page), + }, + }); + }); + + // 分配标注者 + router.post(API.assignAnnotatorUsingPost, (req, res) => { + const { taskId } = req.params; + const { annotatorIds } = req.body; + + res.send({ + code: "0", + msg: "Annotators assigned successfully", + data: { + taskId, + assignedAnnotators: annotatorIds, + assignedAt: new Date().toISOString(), + }, + }); + }); +}; diff --git a/frontend/src/mock/mock-seed/data-cleansing.cjs b/frontend/src/mock/mock-seed/data-cleansing.cjs new file mode 100644 index 0000000..6ecf051 --- /dev/null +++ b/frontend/src/mock/mock-seed/data-cleansing.cjs @@ -0,0 +1,544 @@ +const Mock = require("mockjs"); +const API = require("../mock-apis.cjs"); + +function operatorItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(3, 10), + description: Mock.Random.csentence(5, 20), + version: "1.0.0", + inputs: Mock.Random.integer(1, 5), + outputs: Mock.Random.integer(1, 5), + settings: JSON.stringify({ + fileLength: { + name: "文档字数", + description: + "过滤字数不在指定范围内的文档,如[10,10000000]。若输入为空,则不对字数上/下限做限制。", + type: "range", + defaultVal: [10, 10000000], + min: 0, + max: 10000000000000000, + step: 1, + }, + host: { type: "input", name: "主机地址", defaultVal: "localhost" }, + limit: { + type: "range", + name: "读取行数", + defaultVal: [1000, 2000], + min: 100, + max: 10000, + step: 100, + }, + filepath: { type: "input", name: "文件路径", defaultVal: "/path" }, + encoding: { + type: "select", + name: "编码", + defaultVal: "utf-8", + options: ["utf-8", "gbk", "ascii"], + }, + radio: { + type: "radio", + name: "radio", + defaultVal: "utf-8", + options: ["utf-8", "gbk", "ascii"], + }, + features: { + type: "checkbox", + name: "特征列", + defaultVal: ["feature1", "feature3"], + options: ["feature1", "feature2", "feature3"], + }, + repeatPhraseRatio: { + name: "文档词重复率", + description: "某个词的统计数/文档总词数 > 设定值,该文档被去除。", + type: "slider", + defaultVal: 0.5, + min: 0, + max: 1, + step: 0.1, + }, + hitStopwords: { + name: "去除停用词", + description: "统计重复词时,选择是否要去除停用词。", + type: "switch", + defaultVal: false, + required: true, + checkedLabel: "去除", + unCheckedLabel: "不去除", + }, + }), + categories: [Mock.Random.pick([3, 4, 5, 6, 7, 8, 9])], + isStar: Mock.Random.boolean(), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + }; +} + +const operatorList = new Array(50).fill(null).map(operatorItem); + +// 清洗任务数据 +function cleaningTaskItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(5, 20), + description: Mock.Random.csentence(5, 30), + status: Mock.Random.pick([ + "PENDING", + "RUNNING", + "COMPLETED", + "FAILED", + "STOPPED", + ]), + srcDatasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + srcDatasetName: Mock.Random.ctitle(5, 15), + destDatasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + destDatasetName: Mock.Random.ctitle(5, 15), + progress: Mock.Random.float(0, 100, 2, 2), + startedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + endedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + instance: operatorList, + }; +} + +const cleaningTaskList = new Array(20).fill(null).map(cleaningTaskItem); + +// 清洗模板数据 +function cleaningTemplateItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(5, 15), + description: Mock.Random.csentence(5, 25), + instance: operatorList.slice( + Mock.Random.integer(0, 5), + Mock.Random.integer(6, 50) + ), + category: Mock.Random.ctitle(3, 8), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + }; +} + +const cleaningTemplateList = new Array(15).fill(null).map(cleaningTemplateItem); + +const categoryTree = [ + { + id: 1, + name: "modal", + count: 7, + categories: [ + { id: 3, name: "text", count: 3, type: null, parentId: null }, + { id: 4, name: "image", count: 0, type: null, parentId: null }, + { id: 5, name: "audio", count: 0, type: null, parentId: null }, + { id: 6, name: "video", count: 0, type: null, parentId: null }, + { + id: 7, + name: "multimodal", + count: 0, + type: null, + parentId: null, + }, + ], + }, + { + id: 2, + name: "language", + count: 3, + categories: [ + { id: 8, name: "python", count: 2, type: null, parentId: null }, + { id: 9, name: "java", count: 1, type: null, parentId: null }, + ], + }, +]; + +module.exports = function (router) { + // 获取清洗任务列表 + router.get(API.queryCleaningTasksUsingGet, (req, res) => { + const { page = 0, size = 10, status } = req.query; + let filteredTasks = cleaningTaskList; + + if (status) { + filteredTasks = cleaningTaskList.filter((task) => task.status === status); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredTasks.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredTasks.length, + totalPages: Math.ceil(filteredTasks.length / size), + size: parseInt(size), + number: parseInt(page), + }, + }); + }); + + // 创建清洗任务 + router.post(API.createCleaningTaskUsingPost, (req, res) => { + const newTask = { + ...cleaningTaskItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + status: "PENDING", + createdAt: new Date().toISOString(), + }; + cleaningTaskList.push(newTask); + + res.status(201).send({ + code: "0", + msg: "Cleaning task created successfully", + data: newTask, + }); + }); + + // 获取清洗任务详情 + router.get(API.queryCleaningTaskByIdUsingGet, (req, res) => { + const { taskId } = req.params; + const task = cleaningTaskList.find((j) => j.id === taskId); + + if (task) { + res.send({ + code: "0", + msg: "Success", + data: task, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Cleaning task not found", + data: null, + }); + } + }); + + // 删除清洗任务 + router.delete(API.deleteCleaningTaskByIdUsingDelete, (req, res) => { + const { taskId } = req.params; + const index = cleaningTaskList.findIndex((j) => j.id === taskId); + + if (index !== -1) { + cleaningTaskList.splice(index, 1); + res.send({ + code: "0", + msg: "Cleaning task deleted successfully", + data: null, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Cleaning task not found", + data: null, + }); + } + }); + + // 执行清洗任务 + router.post(API.executeCleaningTaskUsingPost, (req, res) => { + const { taskId } = req.params; + const task = cleaningTaskList.find((j) => j.id === taskId); + + if (task) { + task.status = "running"; + task.startTime = new Date().toISOString(); + + res.send({ + code: "0", + msg: "Cleaning task execution started", + data: { + executionId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + status: "running", + message: "Task execution started successfully", + }, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Cleaning task not found", + data: null, + }); + } + }); + + // 停止清洗任务 + router.post(API.stopCleaningTaskUsingPost, (req, res) => { + const { taskId } = req.params; + const task = cleaningTaskList.find((j) => j.id === taskId); + + if (task) { + task.status = "pending"; + task.endTime = new Date().toISOString(); + + res.send({ + code: "0", + msg: "Cleaning task stopped successfully", + data: null, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Cleaning task not found", + data: null, + }); + } + }); + + // 获取清洗模板列表 + router.get(API.queryCleaningTemplatesUsingGet, (req, res) => { + const { page = 0, size = 20 } = req.query; + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = cleaningTemplateList.slice(startIndex, endIndex); + res.send({ + code: "0", + msg: "Success", + data: { content: pageData, totalElements: cleaningTemplateList.length }, + }); + }); + + // 创建清洗模板 + router.post(API.createCleaningTemplateUsingPost, (req, res) => { + const newTemplate = { + ...cleaningTemplateItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + createdAt: new Date().toISOString(), + }; + cleaningTemplateList.push(newTemplate); + + res.status(201).send({ + code: "0", + msg: "Cleaning template created successfully", + data: newTemplate, + }); + }); + + // 获取清洗模板详情 + router.get(API.queryCleaningTemplateByIdUsingGet, (req, res) => { + const { templateId } = req.params; + const template = cleaningTemplateList.find((t) => t.id === templateId); + + if (template) { + res.send({ + code: "0", + msg: "Success", + data: template, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Cleaning template not found", + data: null, + }); + } + }); + + // 删除清洗模板 + router.delete(API.deleteCleaningTemplateByIdUsingDelete, (req, res) => { + const { templateId } = req.params; + const index = cleaningTemplateList.findIndex((t) => t.id === templateId); + + if (index !== -1) { + cleaningTemplateList.splice(index, 1); + res.send({ + code: "0", + msg: "Cleaning template deleted successfully", + data: null, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Cleaning template not found", + data: null, + }); + } + }); + + // 获取算子列表 + router.post(API.queryOperatorsUsingPost, (req, res) => { + const { + page = 0, + size = 20, + categories = [], + operatorName = "", + labelName = "", + isStar, + } = req.body; + + let filteredOperators = operatorList; + + // 按分类筛选 + if (categories && categories.length > 0) { + filteredOperators = filteredOperators.filter((op) => + categories.includes(op.category.id) + ); + } + + // 按名称搜索 + if (operatorName) { + filteredOperators = filteredOperators.filter((op) => + op.name.toLowerCase().includes(operatorName.toLowerCase()) + ); + } + + // 按标签筛选 + if (labelName) { + filteredOperators = filteredOperators.filter((op) => + op.labels.some((label) => label.name.includes(labelName)) + ); + } + + // 按收藏状态筛选 + if (typeof isStar === "boolean") { + filteredOperators = filteredOperators.filter( + (op) => op.isStar === isStar + ); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredOperators.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredOperators.length, + totalPages: Math.ceil(filteredOperators.length / size), + size: parseInt(size), + number: parseInt(page), + first: page === 0, + last: page >= Math.ceil(filteredOperators.length / size) - 1, + }, + }); + }); + + // 获取算子详情 + router.get(API.queryOperatorByIdUsingGet, (req, res) => { + const { id } = req.params; + const operator = operatorList.find((op) => op.id === id); + + if (operator) { + // 增加浏览次数模拟 + operator.viewCount = (operator.viewCount || 0) + 1; + + res.send({ + code: "0", + msg: "Success", + data: operator, + }); + } else { + res.status(404).send({ + error: "OPERATOR_NOT_FOUND", + message: "算子不存在", + timestamp: new Date().toISOString(), + }); + } + }); + + // 更新算子信息 + router.put(API.updateOperatorByIdUsingPut, (req, res) => { + const { id } = req.params; + const index = operatorList.findIndex((op) => op.id === id); + + if (index !== -1) { + operatorList[index] = { + ...operatorList[index], + ...req.body, + updatedAt: new Date().toISOString(), + }; + + res.send({ + code: "0", + msg: "Operator updated successfully", + data: operatorList[index], + }); + } else { + res.status(404).send({ + error: "OPERATOR_NOT_FOUND", + message: "算子不存在", + timestamp: new Date().toISOString(), + }); + } + }); + + // 创建算子 + router.post(API.createOperatorUsingPost, (req, res) => { + const { name, description, version, category, documentation } = req.body; + + const newOperator = { + ...operatorItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name, + description, + version, + category: + typeof category === "string" + ? { id: category, name: category } + : category, + documentation, + status: "REVIEWING", + downloadCount: 0, + rating: 0, + ratingCount: 0, + isStar: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + operatorList.push(newOperator); + + res.status(201).send({ + code: "0", + msg: "Operator created successfully", + data: newOperator, + }); + }); + + // 上传算子 + router.post(API.uploadOperatorUsingPost, (req, res) => { + const { description } = req.body; + + const newOperator = { + ...operatorItem(), + description: description || "通过文件上传创建的算子", + status: "REVIEWING", + downloadCount: 0, + rating: 0, + ratingCount: 0, + isStar: false, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + operatorList.push(newOperator); + + res.status(201).send({ + code: "0", + msg: "Operator uploaded successfully", + data: newOperator, + }); + }); + + // 获取算子分类树 + router.get(API.queryCategoryTreeUsingGet, (req, res) => { + res.send({ + code: "0", + msg: "Success", + data: { + page: 0, + size: categoryTree.length, + totalElements: categoryTree.length, + totalPages: 1, + content: categoryTree, + }, + }); + }); +}; diff --git a/frontend/src/mock/mock-seed/data-collection.cjs b/frontend/src/mock/mock-seed/data-collection.cjs new file mode 100644 index 0000000..5d0797a --- /dev/null +++ b/frontend/src/mock/mock-seed/data-collection.cjs @@ -0,0 +1,231 @@ +const Mock = require("mockjs"); +const API = require("../mock-apis.cjs"); +const { Random } = Mock; + +// 生成模拟数据归集统计信息 +const collectionStatistics = { + period: Random.pick(["HOUR", "DAY", "WEEK", "MONTH"]), + totalTasks: Random.integer(50, 200), + activeTasks: Random.integer(10, 50), + successfulExecutions: Random.integer(30, 150), + failedExecutions: Random.integer(0, 50), + totalExecutions: Random.integer(20, 100), + avgExecutionTime: Random.integer(1000, 10000), // in milliseconds + avgThroughput: Random.integer(100, 1000), // records per second + topDataSources: new Array(5).fill(null).map(() => ({ + dataSourceId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + dataSourceName: Mock.Random.word(5, 15), + type: Mock.Random.pick([ + "MySQL", + "PostgreSQL", + "ORACLE", + "SQLSERVER", + "MONGODB", + "REDIS", + "ELASTICSEARCH", + "HIVE", + "HDFS", + "KAFKA", + "HTTP", + "FILE", + ]), + taskCount: Mock.Random.integer(1, 20), + executionCount: Mock.Random.integer(1, 50), + recordsProcessed: Mock.Random.integer(70, 100), // percentage + })), +}; + +// 生成模拟任务数据 +function taskItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(5, 20), + description: Mock.Random.csentence(5, 20), + syncMode: Mock.Random.pick(["ONCE", "SCHEDULED"]), + config: { + query: "SELECT * FROM table WHERE condition", + batchSize: Mock.Random.integer(100, 1000), + frequency: Mock.Random.integer(1, 60), // in minutes + }, + scheduleExpression: "0 0 * * *", // cron expression + lastExecutionId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + status: Mock.Random.pick([ + "DRAFT", + "READY", + "RUNNING", + "FAILED", + "STOPPED", + "SUCCESS", + ]), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + sourceDataSourceId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + sourceDataSourceName: Mock.Random.ctitle(5, 20), + targetDataSourceId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + targetDataSourceName: Mock.Random.ctitle(5, 20), + }; +} + +const taskList = new Array(50).fill(null).map(taskItem); + +// 生成模拟任务执行日志数据 +function executionLogItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + taskName: Mock.Random.ctitle(5, 20), + dataSource: Mock.Random.ctitle(5, 15), + startTime: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + endTime: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + status: Mock.Random.pick(["SUCCESS", "FAILED", "RUNNING"]), + triggerType: Mock.Random.pick(["MANUAL", "SCHEDULED", "API"]), + duration: Mock.Random.integer(1, 120), + retryCount: Mock.Random.integer(0, 5), + recordsProcessed: Mock.Random.integer(100, 10000), + processId: Mock.Random.integer(1000, 9999), + errorMessage: Mock.Random.boolean() ? "" : Mock.Random.csentence(5, 20), + }; +} + +const executionLogList = new Array(100).fill(null).map(executionLogItem); + +module.exports = function (router) { + // 获取数据统计信息 + router.get(API.queryCollectionStatisticsUsingGet, (req, res) => { + res.send({ + code: "0", + msg: "Success", + data: collectionStatistics, + }); + }); + + // 获取任务列表 + router.post(API.queryTasksUsingPost, (req, res) => { + const { searchTerm, filters, page = 1, size = 10 } = req.body; + let filteredTasks = taskList; + if (searchTerm) { + filteredTasks = filteredTasks.filter((task) => + task.name.includes(searchTerm) + ); + } + if (filters && filters.status && filters.status.length > 0) { + filteredTasks = filteredTasks.filter((task) => + filters.status.includes(task.status) + ); + } + const startIndex = (page - 1) * size; + const endIndex = startIndex + size; + const paginatedTasks = filteredTasks.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + totalElements: filteredTasks.length, + page, + size, + results: paginatedTasks, + }, + }); + }); + + // 创建任务 + router.post(API.createTaskUsingPost, (req, res) => { + taskList.unshift(taskItem()); // 添加一个新的任务到列表开头 + res.send({ + code: "0", + msg: "任务创建成功", + data: { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + }, + }); + }); + + // 更新任务 + router.post(API.updateTaskByIdUsingPut, (req, res) => { + const { id } = req.body; + res.send({ + code: "0", + msg: "Data source task updated successfully", + data: taskList.find((task) => task.id === id), + }); + }); + + // 删除任务 + router.post(API.deleteTaskByIdUsingDelete, (req, res) => { + const { id } = req.body; + const index = taskList.findIndex((task) => task.id === id); + if (index !== -1) { + taskList.splice(index, 1); + } + res.send({ + code: "0", + msg: "Data source task deleted successfully", + data: null, + }); + }); + + // 执行任务 + router.post(API.executeTaskByIdUsingPost, (req, res) => { + console.log("Received request to execute task", req.body); + const { id } = req.body; + console.log("Executing task with ID:", id); + taskList.find((task) => task.id === id).status = "RUNNING"; + res.send({ + code: "0", + msg: "Data source task execution started", + data: null, + }); + }); + + // 停止任务 + router.post(API.stopTaskByIdUsingPost, (req, res) => { + const { id } = req.body; + const task = taskList.find((task) => task.id === id); + if (task) { + task.status = "STOPPED"; + } + res.send({ + code: "0", + msg: "Data source task stopped successfully", + data: null, + }); + }); + + // 获取任务执行日志 + router.post(API.queryExecutionLogUsingPost, (req, res) => { + const { keyword, page = 1, size = 10, status } = req.body; + let filteredLogs = executionLogList; + if (keyword) { + filteredLogs = filteredLogs.filter((log) => + log.taskName.includes(keyword) + ); + } + if (status && status.length > 0) { + filteredLogs = filteredLogs.filter((log) => status.includes(log.status)); + } + const startIndex = (page - 1) * size; + const endIndex = startIndex + size; + const paginatedLogs = filteredLogs.slice(startIndex, endIndex); + res.send({ + code: "0", + msg: "Success", + data: { + totalElements: filteredLogs.length, + page, + size, + results: paginatedLogs, + }, + }); + }); + + // 获取任务执行日志详情 + router.post(API.queryExecutionLogByIdUsingGet, (req, res) => { + const { id } = req.body; + const log = executionLogList.find((log) => log.id === id); + res.send({ + code: "0", + msg: "Success", + data: log, + }); + }); +}; diff --git a/frontend/src/mock/mock-seed/data-evaluation.cjs b/frontend/src/mock/mock-seed/data-evaluation.cjs new file mode 100644 index 0000000..0c96a4c --- /dev/null +++ b/frontend/src/mock/mock-seed/data-evaluation.cjs @@ -0,0 +1,501 @@ +const Mock = require("mockjs"); +const API = require("../mock-apis.cjs"); + +// 质量指标枚举 +const QualityMetrics = [ + "COMPLETENESS", + "ACCURACY", + "CONSISTENCY", + "VALIDITY", + "UNIQUENESS", + "TIMELINESS" +]; + +// 适配性标准枚举 +const CompatibilityCriteria = [ + "FORMAT_COMPATIBILITY", + "SCHEMA_COMPATIBILITY", + "SIZE_ADEQUACY", + "DISTRIBUTION_MATCH", + "FEATURE_COVERAGE" +]; + +// 价值标准枚举 +const ValueCriteria = [ + "RARITY", + "DEMAND", + "QUALITY", + "COMPLETENESS", + "TIMELINESS", + "STRATEGIC_IMPORTANCE" +]; + +// 评估类型枚举 +const EvaluationTypes = ["QUALITY", "COMPATIBILITY", "VALUE", "COMPREHENSIVE"]; + +// 评估状态枚举 +const EvaluationStatuses = ["PENDING", "RUNNING", "COMPLETED", "FAILED"]; + +// 生成质量评估结果 +function qualityEvaluationItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + datasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + status: Mock.Random.pick(EvaluationStatuses), + overallScore: Mock.Random.float(0.6, 1.0, 2, 2), + metrics: Mock.Random.shuffle(QualityMetrics).slice(0, Mock.Random.integer(3, 5)).map(metric => ({ + metric, + score: Mock.Random.float(0.5, 1.0, 2, 2), + details: { + totalRecords: Mock.Random.integer(1000, 100000), + validRecords: Mock.Random.integer(800, 95000), + issues: Mock.Random.integer(0, 50) + }, + issues: new Array(Mock.Random.integer(0, 3)).fill(null).map(() => ({ + type: Mock.Random.pick(["MISSING_VALUE", "INVALID_FORMAT", "DUPLICATE", "OUTLIER"]), + severity: Mock.Random.pick(["LOW", "MEDIUM", "HIGH", "CRITICAL"]), + description: Mock.Random.csentence(5, 15), + affectedRecords: Mock.Random.integer(1, 1000), + suggestions: [Mock.Random.csentence(5, 20)] + })) + })), + recommendations: new Array(Mock.Random.integer(2, 5)).fill(null).map(() => + Mock.Random.csentence(10, 30) + ), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + detailedResults: { + fieldAnalysis: new Array(Mock.Random.integer(3, 8)).fill(null).map(() => ({ + fieldName: Mock.Random.word(5, 10), + dataType: Mock.Random.pick(["STRING", "INTEGER", "FLOAT", "BOOLEAN", "DATE"]), + nullCount: Mock.Random.integer(0, 100), + uniqueCount: Mock.Random.integer(100, 1000), + statistics: { + mean: Mock.Random.float(0, 100, 2, 2), + median: Mock.Random.float(0, 100, 2, 2), + stdDev: Mock.Random.float(0, 50, 2, 2) + } + })), + distributionAnalysis: { + distributions: new Array(3).fill(null).map(() => ({ + field: Mock.Random.word(5, 10), + type: Mock.Random.pick(["NORMAL", "UNIFORM", "SKEWED"]), + parameters: {} + })), + outliers: new Array(Mock.Random.integer(0, 5)).fill(null).map(() => ({ + field: Mock.Random.word(5, 10), + value: Mock.Random.float(-100, 100, 2, 2), + zScore: Mock.Random.float(-3, 3, 2, 2) + })), + patterns: [ + "数据分布较为均匀", + "存在少量异常值", + "部分字段相关性较强" + ] + }, + correlationAnalysis: { + correlationMatrix: new Array(5).fill(null).map(() => + new Array(5).fill(null).map(() => Mock.Random.float(-1, 1, 2, 2)) + ), + significantCorrelations: new Array(Mock.Random.integer(1, 3)).fill(null).map(() => ({ + field1: Mock.Random.word(5, 10), + field2: Mock.Random.word(5, 10), + correlation: Mock.Random.float(0.5, 1, 2, 2), + pValue: Mock.Random.float(0, 0.05, 3, 3) + })) + } + }, + visualizations: new Array(Mock.Random.integer(2, 4)).fill(null).map(() => ({ + type: Mock.Random.pick(["CHART", "GRAPH", "HISTOGRAM", "HEATMAP"]), + title: Mock.Random.ctitle(5, 15), + data: { + labels: new Array(5).fill(null).map(() => Mock.Random.word(3, 8)), + values: new Array(5).fill(null).map(() => Mock.Random.integer(0, 100)) + }, + config: { + width: 400, + height: 300, + color: Mock.Random.color() + } + })) + }; +} + +// 生成适配性评估结果 +function compatibilityEvaluationItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + datasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + targetType: Mock.Random.pick(["LANGUAGE_MODEL", "CLASSIFICATION_MODEL", "RECOMMENDATION_SYSTEM", "CUSTOM_TASK"]), + compatibilityScore: Mock.Random.float(0.6, 1.0, 2, 2), + results: Mock.Random.shuffle(CompatibilityCriteria).slice(0, Mock.Random.integer(3, 5)).map(criterion => ({ + criterion, + score: Mock.Random.float(0.5, 1.0, 2, 2), + status: Mock.Random.pick(["PASS", "WARN", "FAIL"]), + details: Mock.Random.csentence(10, 30) + })), + suggestions: new Array(Mock.Random.integer(2, 4)).fill(null).map(() => + Mock.Random.csentence(10, 25) + ), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss") + }; +} + +// 生成价值评估结果 +function valueEvaluationItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + datasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + valueScore: Mock.Random.float(0.6, 1.0, 2, 2), + monetaryValue: Mock.Random.float(10000, 1000000, 2, 2), + strategicValue: Mock.Random.float(0.6, 1.0, 2, 2), + results: Mock.Random.shuffle(ValueCriteria).slice(0, Mock.Random.integer(3, 5)).map(criterion => ({ + criterion, + score: Mock.Random.float(0.5, 1.0, 2, 2), + impact: Mock.Random.pick(["LOW", "MEDIUM", "HIGH"]), + explanation: Mock.Random.csentence(10, 30) + })), + insights: new Array(Mock.Random.integer(3, 6)).fill(null).map(() => + Mock.Random.csentence(15, 40) + ), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss") + }; +} + +// 生成评估报告 +function evaluationReportItem() { + const type = Mock.Random.pick(EvaluationTypes); + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + datasetId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + datasetName: Mock.Random.ctitle(5, 15), + type, + status: Mock.Random.pick(EvaluationStatuses), + overallScore: Mock.Random.float(0.6, 1.0, 2, 2), + summary: Mock.Random.csentence(20, 50), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + completedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + qualityResults: type === "QUALITY" || type === "COMPREHENSIVE" ? qualityEvaluationItem() : null, + compatibilityResults: type === "COMPATIBILITY" || type === "COMPREHENSIVE" ? compatibilityEvaluationItem() : null, + valueResults: type === "VALUE" || type === "COMPREHENSIVE" ? valueEvaluationItem() : null, + attachments: new Array(Mock.Random.integer(1, 3)).fill(null).map(() => ({ + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.word(5, 10) + "." + Mock.Random.pick(["pdf", "xlsx", "json"]), + type: Mock.Random.pick(["PDF", "EXCEL", "JSON"]), + size: Mock.Random.integer(1024, 1024 * 1024), + downloadUrl: "/api/v1/evaluation/attachments/" + Mock.Random.guid() + })) + }; +} + +const qualityEvaluationList = new Array(30).fill(null).map(qualityEvaluationItem); +const compatibilityEvaluationList = new Array(20).fill(null).map(compatibilityEvaluationItem); +const valueEvaluationList = new Array(25).fill(null).map(valueEvaluationItem); +const evaluationReportList = new Array(50).fill(null).map(evaluationReportItem); + +module.exports = function (router) { + // 数据质量评估 + router.post(API.evaluateDataQualityUsingPost, (req, res) => { + const { datasetId, metrics, sampleSize, parameters } = req.body; + + const newEvaluation = { + ...qualityEvaluationItem(), + datasetId, + status: "RUNNING", + metrics: metrics.map(metric => ({ + metric, + score: Mock.Random.float(0.5, 1.0, 2, 2), + details: { + totalRecords: sampleSize || Mock.Random.integer(1000, 100000), + validRecords: Mock.Random.integer(800, 95000), + issues: Mock.Random.integer(0, 50) + }, + issues: [] + })), + createdAt: new Date().toISOString() + }; + + qualityEvaluationList.push(newEvaluation); + + // 模拟异步处理,2秒后完成 + setTimeout(() => { + newEvaluation.status = "COMPLETED"; + }, 2000); + + res.send({ + code: "0", + msg: "Quality evaluation started successfully", + data: newEvaluation + }); + }); + + // 获取质量评估结果 + router.get(API.getQualityEvaluationByIdUsingGet, (req, res) => { + const { evaluationId } = req.params; + const evaluation = qualityEvaluationList.find(e => e.id === evaluationId); + + if (evaluation) { + res.send({ + code: "0", + msg: "Success", + data: evaluation + }); + } else { + res.status(404).send({ + code: "1", + msg: "Quality evaluation not found", + data: null + }); + } + }); + + // 适配性评估 + router.post(API.evaluateCompatibilityUsingPost, (req, res) => { + const { datasetId, targetType, targetConfig, evaluationCriteria } = req.body; + + const newEvaluation = { + ...compatibilityEvaluationItem(), + datasetId, + targetType, + results: evaluationCriteria.map(criterion => ({ + criterion, + score: Mock.Random.float(0.5, 1.0, 2, 2), + status: Mock.Random.pick(["PASS", "WARN", "FAIL"]), + details: Mock.Random.csentence(10, 30) + })), + createdAt: new Date().toISOString() + }; + + compatibilityEvaluationList.push(newEvaluation); + + res.send({ + code: "0", + msg: "Compatibility evaluation completed successfully", + data: newEvaluation + }); + }); + + // 价值评估 + router.post(API.evaluateValueUsingPost, (req, res) => { + const { datasetId, valueCriteria, marketContext, businessContext } = req.body; + + const newEvaluation = { + ...valueEvaluationItem(), + datasetId, + results: valueCriteria.map(criterion => ({ + criterion, + score: Mock.Random.float(0.5, 1.0, 2, 2), + impact: Mock.Random.pick(["LOW", "MEDIUM", "HIGH"]), + explanation: Mock.Random.csentence(10, 30) + })), + createdAt: new Date().toISOString() + }; + + valueEvaluationList.push(newEvaluation); + + res.send({ + code: "0", + msg: "Value evaluation completed successfully", + data: newEvaluation + }); + }); + + // 获取评估报告列表 + router.get(API.queryEvaluationReportsUsingGet, (req, res) => { + const { page = 0, size = 20, type, datasetId } = req.query; + let filteredReports = evaluationReportList; + + if (type) { + filteredReports = filteredReports.filter(report => report.type === type); + } + + if (datasetId) { + filteredReports = filteredReports.filter(report => report.datasetId === datasetId); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredReports.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredReports.length, + totalPages: Math.ceil(filteredReports.length / size), + size: parseInt(size), + number: parseInt(page) + } + }); + }); + + // 获取评估报告详情 + router.get(API.getEvaluationReportByIdUsingGet, (req, res) => { + const { reportId } = req.params; + const report = evaluationReportList.find(r => r.id === reportId); + + if (report) { + res.send({ + code: "0", + msg: "Success", + data: report + }); + } else { + res.status(404).send({ + code: "1", + msg: "Evaluation report not found", + data: null + }); + } + }); + + // 导出评估报告 + router.get(API.exportEvaluationReportUsingGet, (req, res) => { + const { reportId } = req.params; + const { format = "PDF" } = req.query; + const report = evaluationReportList.find(r => r.id === reportId); + + if (report) { + const fileName = `evaluation_report_${reportId}.${format.toLowerCase()}`; + + res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`); + res.setHeader('Content-Type', 'application/octet-stream'); + res.send(`Mock ${format} content for evaluation report ${reportId}`); + } else { + res.status(404).send({ + code: "1", + msg: "Evaluation report not found", + data: null + }); + } + }); + + // 批量评估 + router.post(API.batchEvaluationUsingPost, (req, res) => { + const { datasetIds, evaluationTypes, parameters } = req.body; + + const batchId = Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""); + const totalTasks = datasetIds.length * evaluationTypes.length; + + // 为每个数据集和评估类型组合创建任务 + datasetIds.forEach(datasetId => { + evaluationTypes.forEach(type => { + const report = { + ...evaluationReportItem(), + datasetId, + type, + status: "PENDING", + batchId + }; + evaluationReportList.push(report); + + // 模拟异步处理 + setTimeout(() => { + report.status = "COMPLETED"; + }, Mock.Random.integer(3000, 10000)); + }); + }); + + res.status(202).send({ + code: "0", + msg: "Batch evaluation submitted successfully", + data: { + batchId, + status: "SUBMITTED", + totalTasks, + submittedAt: new Date().toISOString() + } + }); + }); + + // 获取批量评估状态 + router.get("/api/v1/evaluation/batch/:batchId", (req, res) => { + const { batchId } = req.params; + const batchReports = evaluationReportList.filter(r => r.batchId === batchId); + + const completedTasks = batchReports.filter(r => r.status === "COMPLETED").length; + const runningTasks = batchReports.filter(r => r.status === "RUNNING").length; + const pendingTasks = batchReports.filter(r => r.status === "PENDING").length; + const failedTasks = batchReports.filter(r => r.status === "FAILED").length; + + let overallStatus = "COMPLETED"; + if (runningTasks > 0 || pendingTasks > 0) { + overallStatus = "RUNNING"; + } else if (failedTasks > 0) { + overallStatus = "PARTIAL_FAILED"; + } + + res.send({ + code: "0", + msg: "Success", + data: { + batchId, + status: overallStatus, + totalTasks: batchReports.length, + completedTasks, + runningTasks, + pendingTasks, + failedTasks, + progress: batchReports.length > 0 ? Math.round((completedTasks / batchReports.length) * 100) : 0, + reports: batchReports + } + }); + }); + + // 获取评估统计信息 + router.get("/api/v1/evaluation/statistics", (req, res) => { + const { timeRange = "LAST_30_DAYS" } = req.query; + + const statistics = { + totalEvaluations: evaluationReportList.length, + completedEvaluations: evaluationReportList.filter(r => r.status === "COMPLETED").length, + runningEvaluations: evaluationReportList.filter(r => r.status === "RUNNING").length, + failedEvaluations: evaluationReportList.filter(r => r.status === "FAILED").length, + averageScore: Mock.Random.float(0.75, 0.95, 2, 2), + evaluationTypeDistribution: { + QUALITY: evaluationReportList.filter(r => r.type === "QUALITY").length, + COMPATIBILITY: evaluationReportList.filter(r => r.type === "COMPATIBILITY").length, + VALUE: evaluationReportList.filter(r => r.type === "VALUE").length, + COMPREHENSIVE: evaluationReportList.filter(r => r.type === "COMPREHENSIVE").length + }, + scoreDistribution: { + excellent: evaluationReportList.filter(r => r.overallScore >= 0.9).length, + good: evaluationReportList.filter(r => r.overallScore >= 0.8 && r.overallScore < 0.9).length, + fair: evaluationReportList.filter(r => r.overallScore >= 0.6 && r.overallScore < 0.8).length, + poor: evaluationReportList.filter(r => r.overallScore < 0.6).length + }, + trends: new Array(30).fill(null).map((_, index) => ({ + date: Mock.Random.date("yyyy-MM-dd"), + evaluations: Mock.Random.integer(5, 50), + averageScore: Mock.Random.float(0.7, 0.95, 2, 2) + })) + }; + + res.send({ + code: "0", + msg: "Success", + data: statistics + }); + }); + + // 删除评估报告 + router.delete("/api/v1/evaluation/reports/:reportId", (req, res) => { + const { reportId } = req.params; + const index = evaluationReportList.findIndex(r => r.id === reportId); + + if (index !== -1) { + evaluationReportList.splice(index, 1); + res.send({ + code: "0", + msg: "Evaluation report deleted successfully", + data: null + }); + } else { + res.status(404).send({ + code: "1", + msg: "Evaluation report not found", + data: null + }); + } + }); +}; \ No newline at end of file diff --git a/frontend/src/mock/mock-seed/data-management.cjs b/frontend/src/mock/mock-seed/data-management.cjs new file mode 100644 index 0000000..098439b --- /dev/null +++ b/frontend/src/mock/mock-seed/data-management.cjs @@ -0,0 +1,437 @@ +const Mock = require("mockjs"); +const API = require("../mock-apis.cjs"); + +function tagItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.word(3, 10), + description: Mock.Random.csentence(5, 20), + color: Mock.Random.color(), + usageCount: Mock.Random.integer(0, 100), + }; +} +const tagList = new Array(20).fill(null).map((_, index) => tagItem(index)); + +function datasetItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(5, 20), + type: Mock.Random.pick(["TEXT", "IMAGE", "AUDIO", "VIDEO"]), + status: Mock.Random.pick(["ACTIVE", "INACTIVE", "PROCESSING"]), + tags: Mock.Random.shuffle(tagList).slice(0, Mock.Random.integer(1, 3)), + totalSize: Mock.Random.integer(1024, 1024 * 1024 * 1024), // in bytes + description: Mock.Random.cparagraph(1, 3), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + createdBy: Mock.Random.cname(), + updatedBy: Mock.Random.cname(), + }; +} + +const datasetList = new Array(50) + .fill(null) + .map((_, index) => datasetItem(index)); + +function datasetFileItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + fileName: + Mock.Random.word(5, 15) + + "." + + Mock.Random.pick(["csv", "json", "xml", "parquet", "avro"]), + originName: + Mock.Random.word(5, 15) + + "." + + Mock.Random.pick(["csv", "json", "xml", "parquet", "avro"]), + fileType: Mock.Random.pick(["CSV", "JSON", "XML", "Parquet", "Avro"]), + size: Mock.Random.integer(1024, 1024 * 1024 * 1024), // in bytes + type: Mock.Random.pick(["CSV", "JSON", "XML", "Parquet", "Avro"]), + status: Mock.Random.pick(["UPLOADED", "PROCESSING", "COMPLETED", "ERROR"]), + description: Mock.Random.csentence(5, 20), + filePath: "/path/to/file/" + Mock.Random.word(5, 10), + uploadedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + uploadedBy: Mock.Random.cname(), + }; +} + +const datasetFileList = new Array(200) + .fill(null) + .map((_, index) => datasetFileItem(index)); + +const datasetStatistics = { + count: { + text: 10, + image: 34, + audio: 23, + video: 5, + }, + size: { + text: "120 MB", + image: "3.4 GB", + audio: "2.3 GB", + video: "15 GB", + }, + totalDatasets: datasetList.length, + totalFiles: datasetFileList.length, + completedFiles: datasetFileList.filter((file) => file.status === "COMPLETED") + .length, + totalSize: datasetFileList.reduce((acc, file) => acc + file.size, 0), // in bytes + completionRate: + datasetFileList.length === 0 + ? 0 + : Math.round( + (datasetFileList.filter((file) => file.status === "COMPLETED") + .length / + datasetFileList.length) * + 100 + ), // percentage +}; + +const datasetTypes = [ + { + code: "PRETRAIN", + name: "预训练数据集", + description: "用于模型预训练的大规模数据集", + supportedFormats: ["txt", "json", "csv", "parquet"], + icon: "brain", + }, + { + code: "FINE_TUNE", + name: "微调数据集", + description: "用于模型微调的专业数据集", + supportedFormats: ["json", "csv", "xlsx"], + icon: "tune", + }, + { + code: "EVAL", + name: "评估数据集", + description: "用于模型评估的标准数据集", + supportedFormats: ["json", "csv", "xml"], + icon: "assessment", + }, +]; + +module.exports = { datasetList }; +module.exports = function (router) { + // 获取数据统计信息 + router.get(API.queryDatasetStatisticsUsingGet, (req, res) => { + res.send({ + code: "0", + msg: "Success", + data: datasetStatistics, + }); + }); + + // 创建数据 + router.post(API.createDatasetUsingPost, (req, res) => { + const newDataset = { + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + status: "ACTIVE", + fileCount: 0, + totalSize: 0, + completionRate: 0, + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + createdBy: "Admin", + updatedBy: "Admin", + tags: tagList.filter((tag) => req.body?.tagIds?.includes?.(tag.id)), + }; + datasetList.unshift(newDataset); // Add to the beginning of the list + res.send({ + code: "0", + msg: "Dataset created successfully", + data: newDataset, + }); + }); + + // 获取数据集列表 + router.get(API.queryDatasetsUsingGet, (req, res) => { + const { page = 0, size = 10, keyword, type, status, tags } = req.query; + console.log("Received query params:", req.query); + + let filteredDatasets = datasetList; + if (keyword) { + console.log("filter keyword:", keyword); + + filteredDatasets = filteredDatasets.filter( + (dataset) => + dataset.name.includes(keyword) || + dataset.description.includes(keyword) + ); + } + if (type) { + console.log("filter type:", type); + + filteredDatasets = filteredDatasets.filter( + (dataset) => dataset.type === type + ); + } + if (status) { + console.log("filter status:", status); + filteredDatasets = filteredDatasets.filter( + (dataset) => dataset.status === status + ); + } + if (tags && tags.length > 0) { + console.log("filter tags:", tags); + filteredDatasets = filteredDatasets.filter((dataset) => + tags.every((tag) => dataset.tags.some((t) => t.name === tag)) + ); + } + + const totalElements = filteredDatasets.length; + const paginatedDatasets = filteredDatasets.slice( + page * size, + (page + 1) * size + ); + + res.send({ + code: "0", + msg: "Success", + data: { + totalElements, + page, + size, + content: paginatedDatasets, + }, + }); + }); + + // 根据ID获取数据集详情 + router.get(API.queryDatasetByIdUsingGet, (req, res) => { + const { id } = req.params; + + const dataset = datasetList.find((d) => d.id === id); + if (dataset) { + res.send({ + code: "0", + msg: "Success", + data: dataset, + }); + } else { + res.status(404).send({ + code: "1", + msg: "Dataset not found", + data: null, + }); + } + }); + + // 更新数据集 + router.put(API.updateDatasetByIdUsingPut, (req, res) => { + const { id } = req.params; + let { tags } = req.body; + + const index = datasetList.findIndex((d) => d.id === id); + tags = [...datasetList[index].tags.map((tag) => tag.name), ...tags]; + if (index !== -1) { + datasetList[index] = { + ...datasetList[index], + ...req.body, + tags: tagList.filter((tag) => tags?.includes?.(tag.name)), + updatedAt: new Date().toISOString(), + updatedBy: "Admin", + }; + res.send({ + code: "0", + msg: "Dataset updated successfully", + data: datasetList[index], + }); + } else { + res.status(404).send({ + code: "1", + msg: "Dataset not found", + data: null, + }); + } + }); + + // 删除数据集 + router.delete(API.deleteDatasetByIdUsingDelete, (req, res) => { + const { datasetId } = req.params; + const index = datasetList.findIndex((d) => d.id === datasetId); + + if (index !== -1) { + datasetList.splice(index, 1); + res.status(204).send(); + } else { + res.status(404).send({ + code: "1", + msg: "Dataset not found", + data: null, + }); + } + }); + + // 获取数据集文件列表 + router.get(API.queryFilesUsingGet, (req, res) => { + const { datasetId } = req.params; + const { page = 0, size = 20, fileType, status } = req.query; + + let filteredFiles = datasetFileList; + + if (fileType) { + filteredFiles = filteredFiles.filter( + (file) => file.fileType === fileType + ); + } + + if (status) { + filteredFiles = filteredFiles.filter((file) => file.status === status); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredFiles.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + page: parseInt(page), + size: parseInt(size), + totalElements: filteredFiles.length, + }, + }); + }); + + // 上传文件到数据集 + router.post(API.uploadFileUsingPost, (req, res) => { + const { datasetId } = req.params; + const newFile = { + ...datasetFileItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + uploadedAt: new Date().toISOString(), + uploadedBy: "Admin", + }; + + datasetFileList.push(newFile); + + res.status(201).send({ + code: "0", + msg: "File uploaded successfully", + data: newFile, + }); + }); + + // 获取文件详情 + router.get(API.queryFileByIdUsingGet, (req, res) => { + const { datasetId, fileId } = req.params; + const file = datasetFileList.find((f) => f.id === fileId); + + if (file) { + res.send({ + code: "0", + msg: "Success", + data: file, + }); + } else { + res.status(404).send({ + code: "1", + msg: "File not found", + data: null, + }); + } + }); + + // 删除文件 + router.delete(API.deleteFileByIdUsingDelete, (req, res) => { + const { datasetId, fileId } = req.params; + const index = datasetFileList.findIndex((f) => f.id === fileId); + + if (index !== -1) { + datasetFileList.splice(index, 1); + res.status(204).send(); + } else { + res.status(404).send({ + code: "1", + msg: "File not found", + data: null, + }); + } + }); + + // 下载文件 + router.get(API.downloadFileByIdUsingGet, (req, res) => { + const { datasetId, fileId } = req.params; + const file = datasetFileList.find((f) => f.id === fileId); + + if (file) { + res.setHeader( + "Content-Disposition", + `attachment; filename="${file.fileName}"` + ); + res.setHeader("Content-Type", "application/octet-stream"); + res.send(`Mock file content for ${file.fileName}`); + } else { + res.status(404).send({ + code: "1", + msg: "File not found", + data: null, + }); + } + }); + + // 获取数据集类型列表 + router.get(API.queryDatasetTypesUsingGet, (req, res) => { + res.send({ + code: "0", + msg: "Success", + data: datasetTypes, + }); + }); + + // 获取标签列表 + router.get(API.queryTagsUsingGet, (req, res) => { + const { keyword } = req.query; + let filteredTags = tagList; + + if (keyword) { + filteredTags = tagList.filter((tag) => + tag.name.toLowerCase().includes(keyword.toLowerCase()) + ); + } + + res.send({ + code: "0", + msg: "Success", + data: filteredTags, + }); + }); + + // 创建标签 + router.post(API.createTagUsingPost, (req, res) => { + const newTag = { + ...tagItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + usageCount: 0, + }; + + tagList.push(newTag); + + res.status(201).send({ + code: "0", + msg: "Tag created successfully", + data: newTag, + }); + }); + + router.post(API.preUploadFileUsingPost, (req, res) => { + res.status(201).send(Mock.Random.guid()); + }); + + // 上传 + router.post(API.uploadFileChunkUsingPost, (req, res) => { + res.status(500).send({ message: "Simulated upload failure" }); + // res.status(201).send({ data: "success" }); + }); + + // 取消上传 + router.put(API.cancelUploadUsingPut, (req, res) => { + res.status(201).send({ data: "success" }); + }); +}; diff --git a/frontend/src/mock/mock-seed/data-synthesis.cjs b/frontend/src/mock/mock-seed/data-synthesis.cjs new file mode 100644 index 0000000..826acfc --- /dev/null +++ b/frontend/src/mock/mock-seed/data-synthesis.cjs @@ -0,0 +1,522 @@ +const Mock = require("mockjs"); +const API = require("../mock-apis.cjs"); + +// 合成类型枚举 +const SynthesisTypes = [ + "INSTRUCTION_TUNING", + "COT_DISTILLATION", + "DIALOGUE_GENERATION", + "TEXT_AUGMENTATION", + "MULTIMODAL_SYNTHESIS", + "CUSTOM" +]; + +// 任务状态枚举 +const JobStatuses = ["PENDING", "RUNNING", "COMPLETED", "FAILED", "CANCELLED"]; + +// 模型配置 +function modelConfigItem() { + return { + modelName: Mock.Random.pick([ + "gpt-3.5-turbo", + "gpt-4", + "claude-3", + "llama-2-70b", + "qwen-max" + ]), + temperature: Mock.Random.float(0.1, 1.0, 2, 2), + maxTokens: Mock.Random.pick([512, 1024, 2048, 4096]), + topP: Mock.Random.float(0.1, 1.0, 2, 2), + frequencyPenalty: Mock.Random.float(0, 2.0, 2, 2) + }; +} + +// 合成模板数据 +function synthesisTemplateItem() { + const type = Mock.Random.pick(SynthesisTypes); + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(5, 20), + description: Mock.Random.csentence(5, 30), + type, + category: Mock.Random.pick([ + "教育培训", "对话系统", "内容生成", "代码生成", "多模态", "自定义" + ]), + modelConfig: modelConfigItem(), + enabled: Mock.Random.boolean(), + promptTemplate: type === "INSTRUCTION_TUNING" + ? "请根据以下主题生成一个指令:{topic}\n指令应该包含:\n1. 明确的任务描述\n2. 具体的输入要求\n3. 期望的输出格式" + : type === "COT_DISTILLATION" + ? "问题:{question}\n请提供详细的推理步骤,然后给出最终答案。\n推理过程:\n1. 分析问题的关键信息\n2. 应用相关知识和规则\n3. 逐步推导出结论" + : "请根据以下模板生成内容:{template}", + parameters: { + maxLength: Mock.Random.integer(100, 2000), + diversity: Mock.Random.float(0.1, 1.0, 2, 2), + quality: Mock.Random.float(0.7, 1.0, 2, 2) + }, + examples: new Array(Mock.Random.integer(2, 5)).fill(null).map(() => ({ + input: Mock.Random.csentence(10, 30), + output: Mock.Random.csentence(20, 50), + explanation: Mock.Random.csentence(5, 20) + })), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss") + }; +} + +const synthesisTemplateList = new Array(25).fill(null).map(synthesisTemplateItem); + +// 合成任务数据 +function synthesisJobItem() { + const template = Mock.Random.pick(synthesisTemplateList); + const targetCount = Mock.Random.integer(100, 10000); + const generatedCount = Mock.Random.integer(0, targetCount); + const progress = targetCount > 0 ? (generatedCount / targetCount) * 100 : 0; + + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.ctitle(5, 20), + description: Mock.Random.csentence(5, 30), + templateId: template.id, + template: { + id: template.id, + name: template.name, + type: template.type + }, + status: Mock.Random.pick(JobStatuses), + progress: Math.round(progress * 100) / 100, + targetCount, + generatedCount, + startTime: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + endTime: progress >= 100 ? Mock.Random.datetime("yyyy-MM-dd HH:mm:ss") : null, + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + statistics: { + totalGenerated: generatedCount, + successfulGenerated: Math.floor(generatedCount * Mock.Random.float(0.85, 0.98, 2, 2)), + failedGenerated: Math.floor(generatedCount * Mock.Random.float(0.02, 0.15, 2, 2)), + averageLength: Mock.Random.integer(50, 500), + uniqueCount: Math.floor(generatedCount * Mock.Random.float(0.8, 0.95, 2, 2)) + }, + samples: new Array(Math.min(10, generatedCount)).fill(null).map(() => ({ + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + content: Mock.Random.cparagraph(1, 3), + score: Mock.Random.float(0.6, 1.0, 2, 2), + metadata: { + length: Mock.Random.integer(50, 500), + model: template.modelConfig.modelName, + timestamp: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss") + }, + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss") + })) + }; +} + +const synthesisJobList = new Array(30).fill(null).map(synthesisJobItem); + +// 生成指令数据 +function generatedInstructionItem() { + return { + instruction: Mock.Random.csentence(10, 30), + input: Mock.Random.csentence(5, 20), + output: Mock.Random.csentence(10, 40), + quality: Mock.Random.float(0.7, 1.0, 2, 2) + }; +} + +// COT 示例数据 +function cotExampleItem() { + return { + question: Mock.Random.csentence(10, 25) + "?", + reasoning: Mock.Random.cparagraph(2, 4), + answer: Mock.Random.csentence(5, 15) + }; +} + +// 蒸馏COT数据 +function distilledCOTDataItem() { + return { + question: Mock.Random.csentence(10, 25) + "?", + reasoning: Mock.Random.cparagraph(2, 4), + answer: Mock.Random.csentence(5, 15), + confidence: Mock.Random.float(0.7, 1.0, 2, 2) + }; +} + +module.exports = function (router) { + // 获取合成模板列表 + router.get(API.querySynthesisTemplatesUsingGet, (req, res) => { + const { page = 0, size = 20, type } = req.query; + let filteredTemplates = synthesisTemplateList; + + if (type) { + filteredTemplates = synthesisTemplateList.filter( + (template) => template.type === type + ); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredTemplates.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredTemplates.length, + totalPages: Math.ceil(filteredTemplates.length / size), + size: parseInt(size), + number: parseInt(page) + } + }); + }); + + // 创建合成模板 + router.post(API.createSynthesisTemplateUsingPost, (req, res) => { + const newTemplate = { + ...synthesisTemplateItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + enabled: true, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + }; + synthesisTemplateList.push(newTemplate); + + res.status(201).send({ + code: "0", + msg: "Synthesis template created successfully", + data: newTemplate + }); + }); + + // 获取合成模板详情 + router.get(API.querySynthesisTemplateByIdUsingGet, (req, res) => { + const { templateId } = req.params; + const template = synthesisTemplateList.find((t) => t.id === templateId); + + if (template) { + res.send({ + code: "0", + msg: "Success", + data: template + }); + } else { + res.status(404).send({ + code: "1", + msg: "Synthesis template not found", + data: null + }); + } + }); + + // 更新合成模板 + router.put(API.updateSynthesisTemplateByIdUsingPut, (req, res) => { + const { templateId } = req.params; + const index = synthesisTemplateList.findIndex((t) => t.id === templateId); + + if (index !== -1) { + synthesisTemplateList[index] = { + ...synthesisTemplateList[index], + ...req.body, + updatedAt: new Date().toISOString() + }; + res.send({ + code: "0", + msg: "Synthesis template updated successfully", + data: synthesisTemplateList[index] + }); + } else { + res.status(404).send({ + code: "1", + msg: "Synthesis template not found", + data: null + }); + } + }); + + // 删除合成模板 + router.delete(API.deleteSynthesisTemplateByIdUsingDelete, (req, res) => { + const { templateId } = req.params; + const index = synthesisTemplateList.findIndex((t) => t.id === templateId); + + if (index !== -1) { + synthesisTemplateList.splice(index, 1); + res.send({ + code: "0", + msg: "Synthesis template deleted successfully", + data: null + }); + } else { + res.status(404).send({ + code: "1", + msg: "Synthesis template not found", + data: null + }); + } + }); + + // 获取合成任务列表 + router.get(API.querySynthesisJobsUsingGet, (req, res) => { + const { page = 0, size = 20, status } = req.query; + let filteredJobs = synthesisJobList; + + if (status) { + filteredJobs = synthesisJobList.filter((job) => job.status === status); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredJobs.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredJobs.length, + totalPages: Math.ceil(filteredJobs.length / size), + size: parseInt(size), + number: parseInt(page) + } + }); + }); + + // 创建合成任务 + router.post(API.createSynthesisJobUsingPost, (req, res) => { + const { templateId } = req.body; + const template = synthesisTemplateList.find(t => t.id === templateId); + + const newJob = { + ...synthesisJobItem(), + ...req.body, + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + templateId, + template: template ? { + id: template.id, + name: template.name, + type: template.type + } : null, + status: "PENDING", + progress: 0, + generatedCount: 0, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + }; + synthesisJobList.push(newJob); + + res.status(201).send({ + code: "0", + msg: "Synthesis job created successfully", + data: newJob + }); + }); + + // 获取合成任务详情 + router.get(API.querySynthesisJobByIdUsingGet, (req, res) => { + const { jobId } = req.params; + const job = synthesisJobList.find((j) => j.id === jobId); + + if (job) { + const template = synthesisTemplateList.find(t => t.id === job.templateId); + const jobDetail = { + ...job, + template: template || null + }; + + res.send({ + code: "0", + msg: "Success", + data: jobDetail + }); + } else { + res.status(404).send({ + code: "1", + msg: "Synthesis job not found", + data: null + }); + } + }); + + // 删除合成任务 + router.delete(API.deleteSynthesisJobByIdUsingDelete, (req, res) => { + const { jobId } = req.params; + const index = synthesisJobList.findIndex((j) => j.id === jobId); + + if (index !== -1) { + synthesisJobList.splice(index, 1); + res.send({ + code: "0", + msg: "Synthesis job deleted successfully", + data: null + }); + } else { + res.status(404).send({ + code: "1", + msg: "Synthesis job not found", + data: null + }); + } + }); + + // 执行合成任务 + router.post(API.executeSynthesisJobUsingPost, (req, res) => { + const { jobId } = req.params; + const job = synthesisJobList.find((j) => j.id === jobId); + + if (job) { + job.status = "RUNNING"; + job.startTime = new Date().toISOString(); + job.updatedAt = new Date().toISOString(); + + // 模拟异步执行 + setTimeout(() => { + job.status = Mock.Random.pick(["COMPLETED", "FAILED"]); + job.progress = job.status === "COMPLETED" ? 100 : Mock.Random.float(10, 90, 2, 2); + job.generatedCount = Math.floor((job.progress / 100) * job.targetCount); + job.endTime = new Date().toISOString(); + }, Mock.Random.integer(2000, 5000)); + + res.send({ + code: "0", + msg: "Synthesis job execution started", + data: { + executionId: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + status: "RUNNING", + message: "Job execution started successfully" + } + }); + } else { + res.status(404).send({ + code: "1", + msg: "Synthesis job not found", + data: null + }); + } + }); + + // 指令调优数据合成 + router.post(API.instructionTuningUsingPost, (req, res) => { + const { baseInstructions, targetDomain, count, modelConfig, parameters } = req.body; + + const jobId = Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""); + const generatedInstructions = new Array(count).fill(null).map(() => generatedInstructionItem()); + + const statistics = { + totalGenerated: count, + averageQuality: Mock.Random.float(0.8, 0.95, 2, 2), + diversityScore: Mock.Random.float(0.7, 0.9, 2, 2) + }; + + res.send({ + code: "0", + msg: "Instruction tuning completed successfully", + data: { + jobId, + generatedInstructions, + statistics + } + }); + }); + + // COT蒸馏数据合成 + router.post(API.cotDistillationUsingPost, (req, res) => { + const { sourceModel, targetFormat, examples, parameters } = req.body; + + const jobId = Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""); + const processedCount = examples.length; + const successfulCount = Math.floor(processedCount * Mock.Random.float(0.85, 0.98, 2, 2)); + + const distilledData = new Array(successfulCount).fill(null).map(() => distilledCOTDataItem()); + + const statistics = { + totalProcessed: processedCount, + successfulDistilled: successfulCount, + averageConfidence: Mock.Random.float(0.8, 0.95, 2, 2) + }; + + res.send({ + code: "0", + msg: "COT distillation completed successfully", + data: { + jobId, + distilledData, + statistics + } + }); + }); + + // 获取合成任务统计信息 + router.get("/api/v1/synthesis/statistics", (req, res) => { + const statistics = { + totalJobs: synthesisJobList.length, + completedJobs: synthesisJobList.filter(j => j.status === "COMPLETED").length, + runningJobs: synthesisJobList.filter(j => j.status === "RUNNING").length, + failedJobs: synthesisJobList.filter(j => j.status === "FAILED").length, + totalGenerated: synthesisJobList.reduce((sum, job) => sum + job.generatedCount, 0), + averageQuality: Mock.Random.float(0.8, 0.95, 2, 2), + templateTypeDistribution: { + "INSTRUCTION_TUNING": synthesisTemplateList.filter(t => t.type === "INSTRUCTION_TUNING").length, + "COT_DISTILLATION": synthesisTemplateList.filter(t => t.type === "COT_DISTILLATION").length, + "DIALOGUE_GENERATION": synthesisTemplateList.filter(t => t.type === "DIALOGUE_GENERATION").length, + "TEXT_AUGMENTATION": synthesisTemplateList.filter(t => t.type === "TEXT_AUGMENTATION").length, + "MULTIMODAL_SYNTHESIS": synthesisTemplateList.filter(t => t.type === "MULTIMODAL_SYNTHESIS").length, + "CUSTOM": synthesisTemplateList.filter(t => t.type === "CUSTOM").length + }, + dailyGeneration: new Array(7).fill(null).map((_, index) => ({ + date: Mock.Random.date("yyyy-MM-dd"), + count: Mock.Random.integer(100, 5000) + })) + }; + + res.send({ + code: "0", + msg: "Success", + data: statistics + }); + }); + + // 批量操作 + router.post("/api/v1/synthesis/jobs/batch", (req, res) => { + const { action, jobIds } = req.body; + + let successCount = 0; + let failedCount = 0; + + jobIds.forEach(jobId => { + const job = synthesisJobList.find(j => j.id === jobId); + if (job) { + switch(action) { + case "DELETE": + const index = synthesisJobList.findIndex(j => j.id === jobId); + synthesisJobList.splice(index, 1); + successCount++; + break; + case "START": + job.status = "RUNNING"; + job.startTime = new Date().toISOString(); + successCount++; + break; + case "STOP": + job.status = "CANCELLED"; + job.endTime = new Date().toISOString(); + successCount++; + break; + } + } else { + failedCount++; + } + }); + + res.send({ + code: "0", + msg: `Batch ${action.toLowerCase()} completed`, + data: { + total: jobIds.length, + success: successCount, + failed: failedCount + } + }); + }); +}; \ No newline at end of file diff --git a/frontend/src/mock/mock-seed/knowledge-generation.cjs b/frontend/src/mock/mock-seed/knowledge-generation.cjs new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/mock/mock-seed/operator-market.cjs b/frontend/src/mock/mock-seed/operator-market.cjs new file mode 100644 index 0000000..0780e92 --- /dev/null +++ b/frontend/src/mock/mock-seed/operator-market.cjs @@ -0,0 +1,124 @@ +const Mock = require("mockjs"); +const API = require("../mock-apis.cjs"); + +// 算子标签数据 +function labelItem() { + return { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name: Mock.Random.pick([ + "数据清洗", + "特征选择", + "分类算法", + "聚类算法", + "回归分析", + "深度神经网络", + "卷积神经网络", + "循环神经网络", + "注意力机制", + "文本分析", + "图像处理", + "语音识别", + "推荐算法", + "异常检测", + "优化算法", + "集成学习", + "迁移学习", + "强化学习", + "联邦学习", + ]), + usageCount: Mock.Random.integer(1, 500), + createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"), + }; +} + +const labelList = new Array(50).fill(null).map(labelItem); + +module.exports = function (router) { + // 获取算子标签列表 + router.get(API.queryLabelsUsingGet, (req, res) => { + const { page = 0, size = 20, keyword = "" } = req.query; + + let filteredLabels = labelList; + + if (keyword) { + filteredLabels = labelList.filter((label) => + label.name.toLowerCase().includes(keyword.toLowerCase()) + ); + } + + const startIndex = page * size; + const endIndex = startIndex + parseInt(size); + const pageData = filteredLabels.slice(startIndex, endIndex); + + res.send({ + code: "0", + msg: "Success", + data: { + content: pageData, + totalElements: filteredLabels.length, + totalPages: Math.ceil(filteredLabels.length / size), + size: parseInt(size), + number: parseInt(page), + }, + }); + }); + + // 创建标签 + router.post(API.createLabelUsingPost, (req, res) => { + const { name } = req.body; + + const newLabel = { + id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), + name, + usageCount: 0, + createdAt: new Date().toISOString(), + }; + + labelList.push(newLabel); + + res.status(201).send({ + code: "0", + msg: "Label created successfully", + data: newLabel, + }); + }); + + // 批量删除标签 + router.delete(API.deleteLabelsUsingDelete, (req, res) => { + const labelIds = req.body; // 数组形式的标签ID列表 + + let deletedCount = 0; + labelIds.forEach((labelId) => { + const index = labelList.findIndex((label) => label.id === labelId); + if (index !== -1) { + labelList.splice(index, 1); + deletedCount++; + } + }); + + res.status(204).send(); + }); + + // 更新标签 + router.put(API.updateLabelByIdUsingPut, (req, res) => { + const { id } = req.params; + const updates = req.body; // 数组形式的更新数据 + + updates.forEach((update) => { + const index = labelList.findIndex((label) => label.id === update.id); + if (index !== -1) { + labelList[index] = { + ...labelList[index], + ...update, + updatedAt: new Date().toISOString(), + }; + } + }); + + res.send({ + code: "0", + msg: "Labels updated successfully", + data: null, + }); + }); +}; diff --git a/frontend/src/mock/mock.cjs b/frontend/src/mock/mock.cjs new file mode 100644 index 0000000..1e50cd7 --- /dev/null +++ b/frontend/src/mock/mock.cjs @@ -0,0 +1,58 @@ +const express = require('express'); +const fs = require('fs-extra'); +const path = require('path'); +const bodyParser = require('body-parser'); +const { genExpressSession } = require('./mock-core/session-helper.cjs'); +const { + setHeader, + sendJSON, + strongMatch, + errorHandle, +} = require('./mock-middleware/index.cjs'); + + +const { loadAllMockModules } = require('./mock-core/module-loader.cjs'); +const { log } = require('./mock-core/util.cjs'); + +const app = express(); +const router = express.Router(); + +const argv = require('minimist')(process.argv.slice(2)); +const deployUrl = argv['deploy-url'] || '/'; +const deployPath = argv['deploy-path'] || '/'; +const port = argv.port || 8002; +const env = argv.env || 'development'; + +// app静态文件实际目录 +const deployAppPath = path.join(__dirname, deployPath); +preStartCheck(deployAppPath); + +app.use(setHeader); + +// 提供静态文件服务 +app.use(deployUrl, express.static(deployAppPath)); +app.use(bodyParser.json({limit: '1mb'})); +app.use(bodyParser.urlencoded({ extended: false, limit: '1mb' })); +app.use(sendJSON); +app.use(strongMatch); +app.use(genExpressSession()); + +const mockDir = path.join(__dirname, '/mock-seed'); +loadAllMockModules(router, mockDir); +app.use(deployUrl, router); +app.use(errorHandle); + +app.get('/', (req, res) => { + res.sendFile('default response', { root: deployAppPath }); +}); + +app.listen(port, function() { + log(`Mock server is running at http://localhost:${port}${deployUrl} in ${env} mode`); +}) + +function preStartCheck(deployAppPath) { + if(!fs.existsSync(deployAppPath)) { + log(`Error: The path ${deployAppPath} does not exist. Please build the frontend application first.`, 'error'); + process.exit(1); + } +} \ No newline at end of file diff --git a/frontend/src/mock/nodemon.json b/frontend/src/mock/nodemon.json new file mode 100644 index 0000000..90e2cc4 --- /dev/null +++ b/frontend/src/mock/nodemon.json @@ -0,0 +1,22 @@ +{ + "restartable": "rs", + "ignore": [ + ".git", + "node_modules/**/node_modules", + "dist", + "build", + "*.test.js", + "*.spec.js" + ], + "verbose": true, + "watch": ["*.cjs"], + "exec": "node --inspect=0.0.0.0:9229 mock.cjs", + "ext": "js,cjs,json", + "execMap": { + "js": "node --harmony" + }, + "env": { + "NODE_ENV": "development" + }, + "signal": "SIGTERM" +} diff --git a/frontend/src/mock/operator.tsx b/frontend/src/mock/operator.tsx new file mode 100644 index 0000000..db6ccd4 --- /dev/null +++ b/frontend/src/mock/operator.tsx @@ -0,0 +1,196 @@ +export const mockOperators: Operator[] = [ + { + id: 1, + name: "图像预处理算子", + version: "1.2.0", + description: + "支持图像缩放、裁剪、旋转、颜色空间转换等常用预处理操作,优化了内存使用和处理速度", + author: "张三", + category: "图像处理", + modality: ["image"], + type: "preprocessing", + tags: ["图像处理", "预处理", "缩放", "裁剪", "旋转"], + createdAt: "2024-01-15", + lastModified: "2024-01-23", + status: "active", + isFavorited: true, + downloads: 1247, + usage: 856, + framework: "PyTorch", + language: "Python", + size: "2.3MB", + dependencies: ["opencv-python", "pillow", "numpy"], + inputFormat: ["jpg", "png", "bmp", "tiff"], + outputFormat: ["jpg", "png", "tensor"], + performance: { + accuracy: 99.5, + speed: "50ms/image", + memory: "128MB", + }, + }, + { + id: 2, + name: "文本分词算子", + version: "2.1.3", + description: + "基于深度学习的中文分词算子,支持自定义词典,在医学文本上表现优异", + author: "李四", + category: "自然语言处理", + modality: ["text"], + type: "preprocessing", + tags: ["文本处理", "分词", "中文", "NLP", "医学"], + createdAt: "2024-01-10", + lastModified: "2024-01-20", + status: "active", + isFavorited: false, + downloads: 892, + usage: 634, + framework: "TensorFlow", + language: "Python", + size: "15.6MB", + dependencies: ["tensorflow", "jieba", "transformers"], + inputFormat: ["txt", "json", "csv"], + outputFormat: ["json", "txt"], + performance: { + accuracy: 96.8, + speed: "10ms/sentence", + memory: "256MB", + }, + }, + { + id: 3, + name: "音频特征提取", + version: "1.0.5", + description: "提取音频的MFCC、梅尔频谱、色度等特征,支持多种音频格式", + author: "王五", + category: "音频处理", + modality: ["audio"], + type: "preprocessing", + tags: ["音频处理", "特征提取", "MFCC", "频谱分析"], + createdAt: "2024-01-08", + lastModified: "2024-01-18", + status: "active", + isFavorited: true, + downloads: 456, + usage: 312, + framework: "PyTorch", + language: "Python", + size: "8.9MB", + dependencies: ["librosa", "scipy", "numpy"], + inputFormat: ["wav", "mp3", "flac", "m4a"], + outputFormat: ["npy", "json", "csv"], + performance: { + speed: "2x实时", + memory: "64MB", + }, + }, + { + id: 4, + name: "视频帧提取算子", + version: "1.3.2", + description: "高效的视频帧提取算子,支持关键帧检测和均匀采样", + author: "赵六", + category: "视频处理", + modality: ["video"], + type: "preprocessing", + tags: ["视频处理", "帧提取", "关键帧", "采样"], + createdAt: "2024-01-05", + lastModified: "2024-01-22", + status: "active", + isFavorited: false, + downloads: 723, + usage: 445, + framework: "OpenCV", + language: "Python", + size: "12.4MB", + dependencies: ["opencv-python", "ffmpeg-python"], + inputFormat: ["mp4", "avi", "mov", "mkv"], + outputFormat: ["jpg", "png", "npy"], + performance: { + speed: "30fps处理", + memory: "512MB", + }, + }, + { + id: 5, + name: "多模态融合算子", + version: "2.0.1", + description: "支持文本、图像、音频多模态数据融合的深度学习算子", + author: "孙七", + category: "多模态处理", + modality: ["text", "image", "audio"], + type: "training", + tags: ["多模态", "融合", "深度学习", "注意力机制"], + createdAt: "2024-01-12", + lastModified: "2024-01-21", + status: "beta", + isFavorited: false, + downloads: 234, + usage: 156, + framework: "PyTorch", + language: "Python", + size: "45.2MB", + dependencies: ["torch", "transformers", "torchvision", "torchaudio"], + inputFormat: ["json", "jpg", "wav"], + outputFormat: ["tensor", "json"], + performance: { + accuracy: 94.2, + speed: "100ms/sample", + memory: "2GB", + }, + }, + { + id: 6, + name: "模型推理加速", + version: "1.1.0", + description: "基于TensorRT的模型推理加速算子,支持多种深度学习框架", + author: "周八", + category: "模型优化", + modality: ["image", "text"], + type: "inference", + tags: ["推理加速", "TensorRT", "优化", "GPU"], + createdAt: "2024-01-03", + lastModified: "2024-01-19", + status: "active", + isFavorited: true, + downloads: 567, + usage: 389, + framework: "TensorRT", + language: "Python", + size: "23.7MB", + dependencies: ["tensorrt", "pycuda", "numpy"], + inputFormat: ["onnx", "pb", "pth"], + outputFormat: ["tensor", "json"], + performance: { + speed: "5x加速", + memory: "减少40%", + }, + }, + { + id: 7, + name: "数据增强算子", + version: "1.4.1", + description: "丰富的数据增强策略,包括几何变换、颜色变换、噪声添加等", + author: "吴九", + category: "数据增强", + modality: ["image"], + type: "preprocessing", + tags: ["数据增强", "几何变换", "颜色变换", "噪声"], + createdAt: "2024-01-01", + lastModified: "2024-01-17", + status: "active", + isFavorited: false, + downloads: 934, + usage: 678, + framework: "Albumentations", + language: "Python", + size: "6.8MB", + dependencies: ["albumentations", "opencv-python", "numpy"], + inputFormat: ["jpg", "png", "bmp"], + outputFormat: ["jpg", "png", "npy"], + performance: { + speed: "20ms/image", + memory: "32MB", + }, + }, +]; \ No newline at end of file diff --git a/frontend/src/mock/ratio.tsx b/frontend/src/mock/ratio.tsx new file mode 100644 index 0000000..13918fe --- /dev/null +++ b/frontend/src/mock/ratio.tsx @@ -0,0 +1,193 @@ +import type { RatioTask } from "@/pages/RatioTask/ratio"; + +export const mockRatioTasks: RatioTask[] = [ + { + id: 1, + name: "多领域数据配比任务", + status: "completed", + progress: 100, + sourceDatasets: [ + "orig_20250724_64082", + "financial_qa_dataset", + "medical_corpus", + ], + targetCount: 10000, + generatedCount: 10000, + createdAt: "2025-01-24", + ratioType: "dataset", + estimatedTime: "已完成", + quality: 94, + ratioConfigs: [ + { + id: "1", + name: "通用文本", + type: "dataset", + quantity: 4000, + percentage: 40, + source: "orig_20250724_64082", + }, + { + id: "2", + name: "金融问答", + type: "dataset", + quantity: 3000, + percentage: 30, + source: "financial_qa_dataset", + }, + { + id: "3", + name: "医疗语料", + type: "dataset", + quantity: 3000, + percentage: 30, + source: "medical_corpus", + }, + ], + }, + { + id: 2, + name: "标签配比训练集", + status: "running", + progress: 68, + sourceDatasets: ["teacher_model_outputs", "image_text_pairs"], + targetCount: 8000, + generatedCount: 5440, + createdAt: "2025-01-25", + ratioType: "label", + estimatedTime: "剩余 12 分钟", + quality: 89, + ratioConfigs: [ + { + id: "1", + name: "问答", + type: "label", + quantity: 2500, + percentage: 31.25, + source: "teacher_model_outputs_问答", + }, + { + id: "2", + name: "推理", + type: "label", + quantity: 2000, + percentage: 25, + source: "teacher_model_outputs_推理", + }, + { + id: "3", + name: "图像", + type: "label", + quantity: 1800, + percentage: 22.5, + source: "image_text_pairs_图像", + }, + { + id: "4", + name: "描述", + type: "label", + quantity: 1700, + percentage: 21.25, + source: "image_text_pairs_描述", + }, + ], + }, + { + id: 3, + name: "平衡数据集配比", + status: "failed", + progress: 25, + sourceDatasets: ["orig_20250724_64082", "financial_qa_dataset"], + targetCount: 5000, + generatedCount: 1250, + createdAt: "2025-01-25", + ratioType: "dataset", + errorMessage: "数据源连接失败,请检查数据集状态", + ratioConfigs: [ + { + id: "1", + name: "通用文本", + type: "dataset", + quantity: 2500, + percentage: 50, + source: "orig_20250724_64082", + }, + { + id: "2", + name: "金融问答", + type: "dataset", + quantity: 2500, + percentage: 50, + source: "financial_qa_dataset", + }, + ], + }, + { + id: 4, + name: "文本分类配比任务", + status: "pending", + progress: 0, + sourceDatasets: ["text_classification_data", "sentiment_analysis_data"], + targetCount: 6000, + generatedCount: 0, + createdAt: "2025-01-26", + ratioType: "label", + estimatedTime: "预计 15 分钟", + ratioConfigs: [ + { + id: "1", + name: "正面", + type: "label", + quantity: 2000, + percentage: 33.33, + source: "sentiment_analysis_data_正面", + }, + { + id: "2", + name: "负面", + type: "label", + quantity: 2000, + percentage: 33.33, + source: "sentiment_analysis_data_负面", + }, + { + id: "3", + name: "中性", + type: "label", + quantity: 2000, + percentage: 33.33, + source: "sentiment_analysis_data_中性", + }, + ], + }, + { + id: 5, + name: "多模态数据配比", + status: "paused", + progress: 45, + sourceDatasets: ["image_caption_data", "video_description_data"], + targetCount: 12000, + generatedCount: 5400, + createdAt: "2025-01-23", + ratioType: "dataset", + estimatedTime: "已暂停", + quality: 91, + ratioConfigs: [ + { + id: "1", + name: "图像描述", + type: "dataset", + quantity: 7000, + percentage: 58.33, + source: "image_caption_data", + }, + { + id: "2", + name: "视频描述", + type: "dataset", + quantity: 5000, + percentage: 41.67, + source: "video_description_data", + }, + ], + }, +]; diff --git a/frontend/src/mock/synthesis.tsx b/frontend/src/mock/synthesis.tsx new file mode 100644 index 0000000..cc96120 --- /dev/null +++ b/frontend/src/mock/synthesis.tsx @@ -0,0 +1,209 @@ +// Add mock files data +export const mockFiles = [ + { id: "file1", name: "dataset_part_001.jsonl", size: "2.5MB", type: "JSONL" }, + { id: "file2", name: "dataset_part_002.jsonl", size: "2.3MB", type: "JSONL" }, + { id: "file3", name: "dataset_part_003.jsonl", size: "2.7MB", type: "JSONL" }, + { id: "file4", name: "training_data.txt", size: "1.8MB", type: "TXT" }, + { id: "file5", name: "validation_set.csv", size: "856KB", type: "CSV" }, + { id: "file6", name: "test_samples.json", size: "1.2MB", type: "JSON" }, + { id: "file7", name: "raw_text_001.txt", size: "3.1MB", type: "TXT" }, + { id: "file8", name: "raw_text_002.txt", size: "2.9MB", type: "TXT" }, +]; + +export const mockSynthesisTasks: SynthesisTask[] = [ + { + id: 1, + name: "文字生成问答对_判断题", + type: "qa", + status: "completed", + progress: 100, + sourceDataset: "orig_20250724_64082", + targetCount: 1000, + generatedCount: 1000, + createdAt: "2025-01-20", + template: "判断题生成模板", + estimatedTime: "已完成", + quality: 95, + }, + { + id: 2, + name: "知识蒸馏数据集", + type: "distillation", + status: "running", + progress: 65, + sourceDataset: "teacher_model_outputs", + targetCount: 5000, + generatedCount: 3250, + createdAt: "2025-01-22", + template: "蒸馏模板v2", + estimatedTime: "剩余 15 分钟", + quality: 88, + }, + { + id: 3, + name: "多模态对话生成", + type: "multimodal", + status: "failed", + progress: 25, + sourceDataset: "image_text_pairs", + targetCount: 2000, + generatedCount: 500, + createdAt: "2025-01-23", + template: "多模态对话模板", + errorMessage: "模型API调用失败,请检查配置", + }, + { + id: 4, + name: "金融问答数据生成", + type: "qa", + status: "pending", + progress: 0, + sourceDataset: "financial_qa_dataset", + targetCount: 800, + generatedCount: 0, + createdAt: "2025-01-24", + template: "金融问答模板", + estimatedTime: "等待开始", + quality: 0, + }, + { + id: 5, + name: "医疗文本蒸馏", + type: "distillation", + status: "paused", + progress: 45, + sourceDataset: "medical_corpus", + targetCount: 3000, + generatedCount: 1350, + createdAt: "2025-01-21", + template: "医疗蒸馏模板", + estimatedTime: "已暂停", + quality: 92, + }, +]; + +export const mockTemplates: Template[] = [ + { + id: 1, + name: "判断题生成模板", + type: "preset", + category: "问答对生成", + prompt: `根据给定的文本内容,生成一个判断题。 + +文本内容:{text} + +请按照以下格式生成: +1. 判断题:[基于文本内容的判断题] +2. 答案:[对/错] +3. 解释:[简要解释为什么这个答案是正确的] + +要求: +- 判断题应该基于文本的核心内容 +- 答案必须明确且有依据 +- 解释要简洁清晰`, + variables: ["text"], + description: "根据文本内容生成判断题,适用于教育和培训场景", + usageCount: 156, + lastUsed: "2025-01-20", + quality: 95, + }, + { + id: 2, + name: "选择题生成模板", + type: "preset", + category: "问答对生成", + prompt: `基于以下文本,创建一个多选题: + +{text} + +请按照以下格式生成: +问题:[基于文本的问题] +A. [选项A] +B. [选项B] +C. [选项C] +D. [选项D] +正确答案:[A/B/C/D] +解析:[详细解释] + +要求: +- 问题要有一定难度 +- 选项要有迷惑性 +- 正确答案要有充分依据`, + variables: ["text"], + description: "生成多选题的标准模板,适用于考试和评估", + usageCount: 89, + lastUsed: "2025-01-19", + quality: 92, + }, + { + id: 3, + name: "知识蒸馏模板", + type: "preset", + category: "蒸馏数据集", + prompt: `作为学生模型,学习教师模型的输出: + +输入:{input} +教师输出:{teacher_output} + +请模仿教师模型的推理过程和输出格式,生成相似质量的回答。 + +要求: +- 保持教师模型的推理逻辑 +- 输出格式要一致 +- 质量要接近教师模型水平`, + variables: ["input", "teacher_output"], + description: "用于知识蒸馏的模板,帮助小模型学习大模型的能力", + usageCount: 234, + lastUsed: "2025-01-22", + quality: 88, + }, + { + id: 4, + name: "金融问答模板", + type: "custom", + category: "问答对生成", + prompt: `基于金融领域知识,生成专业问答对: + +参考内容:{content} + +生成格式: +问题:[专业的金融问题] +答案:[准确的专业回答] +关键词:[相关金融术语] + +要求: +- 问题具有实用性 +- 答案准确专业 +- 符合金融行业标准`, + variables: ["content"], + description: "专门用于金融领域的问答对生成", + usageCount: 45, + lastUsed: "2025-01-18", + quality: 89, + }, + { + id: 5, + name: "医疗蒸馏模板", + type: "custom", + category: "蒸馏数据集", + prompt: `医疗知识蒸馏模板: + +原始医疗文本:{medical_text} +专家标注:{expert_annotation} + +生成医疗知识点: +1. 核心概念:[提取关键医疗概念] +2. 临床意义:[说明临床应用价值] +3. 注意事项:[重要提醒和禁忌] + +要求: +- 确保医疗信息准确性 +- 遵循医疗伦理规范 +- 适合医学教育使用`, + variables: ["medical_text", "expert_annotation"], + description: "医疗领域专用的知识蒸馏模板", + usageCount: 67, + lastUsed: "2025-01-21", + quality: 94, + }, +]; diff --git a/frontend/src/pages/Agent/Agent.tsx b/frontend/src/pages/Agent/Agent.tsx new file mode 100644 index 0000000..94df683 --- /dev/null +++ b/frontend/src/pages/Agent/Agent.tsx @@ -0,0 +1,480 @@ +import type React from "react"; + +import { useState, useRef, useEffect } from "react"; +import { Card, Input, Button, Badge } from "antd"; +import { HomeOutlined } from "@ant-design/icons"; +import { + MessageSquare, + Send, + Bot, + User, + Sparkles, + Database, + BarChart3, + Settings, + Zap, + CheckCircle, + Clock, + Download, + ArrowLeft, +} from "lucide-react"; +import { useNavigate } from "react-router"; +import DevelopmentInProgress from "@/components/DevelopmentInProgress"; + +interface Message { + id: string; + type: "user" | "assistant"; + content: string; + timestamp: Date; + actions?: Array<{ + type: + | "create_dataset" + | "run_analysis" + | "start_synthesis" + | "export_report"; + label: string; + data?: any; + }>; + status?: "pending" | "completed" | "error"; +} + +interface QuickAction { + id: string; + label: string; + icon: any; + prompt: string; + category: string; +} + +const quickActions: QuickAction[] = [ + { + id: "create_dataset", + label: "创建数据集", + icon: Database, + prompt: "帮我创建一个新的数据集", + category: "数据管理", + }, + { + id: "analyze_quality", + label: "质量分析", + icon: BarChart3, + prompt: "分析我的数据集质量", + category: "数据评估", + }, + { + id: "start_synthesis", + label: "数据合成", + icon: Sparkles, + prompt: "启动数据合成任务", + category: "数据合成", + }, + { + id: "process_data", + label: "数据清洗", + icon: Settings, + prompt: "对数据集进行预处理", + category: "数据清洗", + }, + { + id: "export_report", + label: "导出报告", + icon: Download, + prompt: "导出最新的分析报告", + category: "报告导出", + }, + { + id: "check_status", + label: "查看状态", + icon: Clock, + prompt: "查看所有任务的运行状态", + category: "状态查询", + }, +]; + +const mockResponses = { + 创建数据集: { + content: + "我来帮您创建一个新的数据集。请告诉我以下信息:\n\n1. 数据集名称\n2. 数据类型(图像、文本、问答对等)\n3. 预期数据量\n4. 数据来源\n\n您也可以直接说出您的需求,我会为您推荐最适合的配置。", + actions: [ + { type: "create_dataset", label: "开始创建", data: { step: "config" } }, + ], + }, + 质量分析: { + content: + "正在为您分析数据集质量...\n\n📊 **分析结果概览:**\n- 图像分类数据集:质量分 92/100\n- 问答对数据集:质量分 87/100\n- 多模态数据集:质量分 78/100\n\n🔍 **发现的主要问题:**\n- 23个重复图像\n- 156个格式不正确的问答对\n- 78个图文不匹配项\n\n💡 **改进建议:**\n- 建议进行去重处理\n- 优化问答对格式\n- 重新标注图文匹配项", + actions: [ + { + type: "run_analysis", + label: "查看详细报告", + data: { type: "detailed" }, + }, + ], + }, + 数据合成: { + content: + "我可以帮您启动数据合成任务。目前支持以下合成类型:\n\n🖼️ **图像数据合成**\n- 数据增强(旋转、翻转、亮度调整)\n- 风格迁移\n- GAN生成\n\n📝 **文本数据合成**\n- 同义词替换\n- 回译增强\n- GPT生成\n\n❓ **问答对合成**\n- 基于知识库生成\n- 模板变换\n- 多轮对话生成\n\n请告诉我您需要合成什么类型的数据,以及目标数量。", + actions: [ + { + type: "start_synthesis", + label: "配置合成任务", + data: { step: "config" }, + }, + ], + }, + 导出报告: { + content: + "正在为您准备最新的分析报告...\n\n📋 **可用报告:**\n- 数据质量评估报告(PDF)\n- 数据分布统计报告(Excel)\n- 模型性能评估报告(PDF)\n- 偏见检测报告(PDF)\n- 综合分析报告(PDF + Excel)\n\n✅ 报告已生成完成,您可以选择下载格式。", + actions: [ + { type: "export_report", label: "下载报告", data: { format: "pdf" } }, + ], + }, + 查看状态: { + content: + "📊 **当前任务状态概览:**\n\n🟢 **运行中的任务:**\n- 问答对生成任务:65% 完成\n- 图像质量分析:运行中\n- 知识库构建:等待中\n\n✅ **已完成的任务:**\n- 图像分类数据集创建:已完成\n- PDF文档提取:已完成\n- 训练集配比任务:已完成\n\n⚠️ **需要关注的任务:**\n- 多模态数据合成:暂停(需要用户确认参数)\n\n所有任务运行正常,预计2小时内全部完成。", + actions: [], + }, +}; + +export default function AgentPage() { + return ; + const navigate = useNavigate(); + const [messages, setMessages] = useState([ + { + id: "welcome", + type: "assistant", + content: + "👋 您好!我是 Data Agent,您的AI数据助手。\n\n我可以帮您:\n• 创建和管理数据集\n• 分析数据质量\n• 启动处理任务\n• 生成分析报告\n• 回答数据相关问题\n\n请告诉我您需要什么帮助,或者点击下方的快捷操作开始。", + timestamp: new Date(), + }, + ]); + const [inputValue, setInputValue] = useState(""); + const [isTyping, setIsTyping] = useState(false); + const messagesEndRef = useRef(null); + const inputRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + const handleSendMessage = async (content: string) => { + if (!content.trim()) return; + + const userMessage: Message = { + id: Date.now().toString(), + type: "user", + content: content.trim(), + timestamp: new Date(), + }; + + setMessages((prev) => [...prev, userMessage]); + setInputValue(""); + setIsTyping(true); + + // 模拟AI响应 + setTimeout(() => { + const response = generateResponse(content); + const assistantMessage: Message = { + id: (Date.now() + 1).toString(), + type: "assistant", + content: response.content, + timestamp: new Date(), + actions: response.actions, + }; + + setMessages((prev) => [...prev, assistantMessage]); + setIsTyping(false); + }, 1500); + }; + + const generateResponse = ( + input: string + ): { content: string; actions?: any[] } => { + const lowerInput = input.toLowerCase(); + + if (lowerInput.includes("创建") && lowerInput.includes("数据集")) { + return mockResponses["创建数据集"]; + } else if (lowerInput.includes("质量") || lowerInput.includes("分析")) { + return mockResponses["质量分析"]; + } else if (lowerInput.includes("合成") || lowerInput.includes("生成")) { + return mockResponses["数据合成"]; + } else if (lowerInput.includes("导出") || lowerInput.includes("报告")) { + return mockResponses["导出报告"]; + } else if (lowerInput.includes("状态") || lowerInput.includes("任务")) { + return mockResponses["查看状态"]; + } else if (lowerInput.includes("你好") || lowerInput.includes("帮助")) { + return { + content: + "很高兴为您服务!我是专门为数据集管理设计的AI助手。\n\n我的主要能力包括:\n\n🔧 **数据集操作**\n- 创建、导入、导出数据集\n- 数据预处理和清洗\n- 批量操作和自动化\n\n📊 **智能分析**\n- 数据质量评估\n- 分布统计分析\n- 性能和偏见检测\n\n🤖 **AI增强**\n- 智能数据合成\n- 自动标注建议\n- 知识库构建\n\n请告诉我您的具体需求,我会为您提供最合适的解决方案!", + }; + } else { + return { + content: `我理解您想要「${input}」。让我为您分析一下...\n\n基于您的需求,我建议:\n\n1. 首先确认具体的操作目标\n2. 选择合适的数据集和参数\n3. 执行相应的处理流程\n\n您可以提供更多详细信息,或者选择下方的快捷操作来开始。如果需要帮助,请说"帮助"获取完整功能列表。`, + actions: [ + { type: "run_analysis", label: "开始分析", data: { query: input } }, + ], + }; + } + }; + + const handleQuickAction = (action: QuickAction) => { + handleSendMessage(action.prompt); + }; + + const handleActionClick = (action: any) => { + const actionMessage: Message = { + id: Date.now().toString(), + type: "assistant", + content: `✅ 正在执行「${action.label}」...\n\n操作已启动,您可以在相应的功能模块中查看详细进度。`, + timestamp: new Date(), + status: "completed", + }; + setMessages((prev) => [...prev, actionMessage]); + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(inputValue); + } + }; + + const formatMessage = (content: string) => { + return content.split("\n").map((line, index) => ( +
+ {line ||
} +
+ )); + }; + + const onBack = () => { + navigate("/"); + }; + + return ( +
+
+ {/* Header */} +
+
+
+
+
+ +
+
+

Data Agent

+

+ AI驱动的智能数据助手,通过对话完成复杂数据操作 +

+
+
+ +
+
+
+ +
+
+ {/* Chat Area */} +
+
+
+
+ 对话窗口 +
+ + 在线 +
+
+
+
+ {/* Messages */} +
+
+ {messages.map((message) => ( +
+ {message.type === "assistant" && ( +
+ +
+ )} +
+
+ {formatMessage(message.content)} +
+ {message.actions && message.actions.length > 0 && ( +
+ {message.actions.map((action, index) => ( + + ))} +
+ )} +
+ {message.timestamp.toLocaleTimeString()} +
+
+ {message.type === "user" && ( +
+ +
+ )} +
+ ))} + {isTyping && ( +
+
+ +
+
+
+
+
+
+
+
+
+ )} +
+
+
+ + {/* Input Area */} +
+
+ setInputValue(e.target.value)} + onKeyDown={handleKeyPress} + placeholder="输入您的需求,例如:创建一个图像分类数据集..." + disabled={isTyping} + /> + +
+
+
+
+
+ + {/* Quick Actions Sidebar */} +
+ +
+ 快捷操作 +
+ 点击快速开始常用操作 +
+
+
+ {quickActions.map((action) => ( + + ))} +
+
+ + +
+ 系统状态 +
+
+
+ + AI服务正常 +
+
+ + 3个任务运行中 +
+
+ + 12个数据集就绪 +
+
+ + 响应时间: 0.8s +
+
+
+ + +
+ 使用提示 +
+
+
💡 您可以用自然语言描述需求
+
🔍 支持复杂的多步骤操作
+
📊 可以询问数据统计和分析
+
⚡ 使用快捷操作提高效率
+
+
+ + +
+ +
+
+
+
+
+
+
+ ); +} diff --git a/frontend/src/pages/DataAnnotation/Annotate/AnnotationWorkSpace.tsx b/frontend/src/pages/DataAnnotation/Annotate/AnnotationWorkSpace.tsx new file mode 100644 index 0000000..fb5910d --- /dev/null +++ b/frontend/src/pages/DataAnnotation/Annotate/AnnotationWorkSpace.tsx @@ -0,0 +1,229 @@ +import { useEffect, useState } from "react"; +import { Card, message } from "antd"; +import { Button, Badge, Progress, Checkbox } from "antd"; +import { + ArrowLeft, + FileText, + ImageIcon, + Video, + Music, + Save, + SkipForward, + CheckCircle, + Eye, + Settings, +} from "lucide-react"; +import { mockTasks } from "@/mock/annotation"; +import { Outlet, useNavigate } from "react-router"; + +export default function AnnotationWorkspace() { + const navigate = useNavigate(); + const [task, setTask] = useState(mockTasks[0]); + + const [currentFileIndex, setCurrentFileIndex] = useState(0); + const [annotationProgress, setAnnotationProgress] = useState({ + completed: task.completedCount, + skipped: task.skippedCount, + total: task.totalCount, + }); + + const handleSaveAndNext = () => { + setAnnotationProgress((prev) => ({ + ...prev, + completed: prev.completed + 1, + })); + + if (currentFileIndex < task.totalCount - 1) { + setCurrentFileIndex(currentFileIndex + 1); + } + + message({ + title: "标注已保存", + description: "标注结果已保存,自动跳转到下一个", + }); + }; + + const handleSkipAndNext = () => { + setAnnotationProgress((prev) => ({ + ...prev, + skipped: prev.skipped + 1, + })); + + if (currentFileIndex < task.totalCount - 1) { + setCurrentFileIndex(currentFileIndex + 1); + } + + message({ + title: "已跳过", + description: "已跳过当前项目,自动跳转到下一个", + }); + }; + + const getDatasetTypeIcon = (type: string) => { + switch (type) { + case "text": + return ; + case "image": + return ; + case "video": + return