From 8032a3a98c4d7e483844bc9d1ddae0307871502d Mon Sep 17 00:00:00 2001 From: yuexie <38447111+YanZhuangz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:25:20 +0800 Subject: [PATCH] repo-sync-2024-07-16T11:00:15+0800 (#378) * repo-sync-2024-07-16T11:00:15+0800 * repo-sync-2024-07-17T10:27:38+0800 * Fix/circleci (#338) * fix circleci * update name * update name * update * update * update version * fix golangci-lint * update sf version --------- Co-authored-by: yushiqie <1539578852@qq.com> --- .circleci/config.yml | 90 +- .circleci/deps-config.yml | 32 +- .../Consulting_issue_template.yaml | 57 ++ ...emplate.yaml => Error_issue_template.yaml} | 58 +- CHANGELOG.md | 25 + Makefile | 4 +- build/dockerfile/kuscia-secretflow.Dockerfile | 6 +- cmd/example/datamesh/README.md | 36 + .../example/datamesh}/arrdata.go | 2 +- cmd/example/datamesh/dm_client.go | 94 +- cmd/example/datamesh/dm_server.go | 139 ++- cmd/kuscia/autonomy/autonomy.go | 7 +- cmd/kuscia/confloader/config.go | 2 + cmd/kuscia/confloader/kuscia_config.go | 2 + cmd/kuscia/kusciainit/init.go | 63 +- cmd/kuscia/kusciainit/init_test.go | 9 +- cmd/kuscia/lite/lite.go | 8 +- cmd/kuscia/main.go | 2 +- cmd/kuscia/master/master.go | 7 +- cmd/kuscia/modules/agent.go | 30 +- cmd/kuscia/modules/allinone_operator.go | 4 +- cmd/kuscia/modules/confmanager.go | 2 +- cmd/kuscia/modules/containerd.go | 9 +- cmd/kuscia/modules/controllers.go | 4 +- cmd/kuscia/modules/coredns.go | 2 +- cmd/kuscia/modules/datamesh.go | 2 +- cmd/kuscia/modules/domainroute.go | 2 +- cmd/kuscia/modules/envoy.go | 4 +- cmd/kuscia/modules/interconn.go | 2 +- cmd/kuscia/modules/k3s.go | 4 +- cmd/kuscia/modules/kusciaapi.go | 22 +- cmd/kuscia/modules/metricexporter.go | 2 +- cmd/kuscia/modules/modules.go | 38 +- cmd/kuscia/modules/nodeexporter.go | 4 +- cmd/kuscia/modules/scheduler.go | 2 +- cmd/kuscia/modules/ssexporter.go | 2 +- cmd/kuscia/modules/transport.go | 4 +- cmd/kuscia/start/start.go | 12 +- cmd/kuscia/utils/debug.go | 2 +- ...kuscia.secretflow_clusterdomainroutes.yaml | 14 +- .../kuscia.secretflow_domainroutes.yaml | 14 +- .../kuscia.secretflow_taskresourcegroups.yaml | 1 - .../deploy_master_lite_cn.md | 104 +- .../Docker_deployment_kuscia/deploy_p2p_cn.md | 57 +- .../K8s_master_lite_cn.md | 21 +- .../K8s_deployment_kuscia/K8s_p2p_cn.md | 9 +- docs/deployment/deploy_check.md | 3 + docs/deployment/kuscia_config_cn.md | 84 +- docs/deployment/kuscia_monitor.md | 31 +- docs/development/build_kuscia_cn.md | 4 +- docs/development/register_custom_image.md | 61 +- docs/getting_started/quickstart_cn.md | 2 +- docs/imgs/flight_do_get.png | Bin 0 -> 52522 bytes docs/imgs/flight_do_put.png | Bin 0 -> 29955 bytes docs/reference/apis/datamesh/datacrud_cn.md | 41 + docs/reference/apis/datamesh/domaindata_cn.md | 2 +- docs/reference/apis/domain_cn.md | 110 +- docs/reference/apis/domaindata_cn.md | 80 +- docs/reference/apis/domaindatagrant_cn.md | 86 +- docs/reference/apis/domaindatasource_cn.md | 70 +- docs/reference/apis/domainroute_cn.md | 88 +- docs/reference/apis/health_cn.md | 10 +- docs/reference/apis/kusciajob_cn.md | 198 ++-- docs/reference/apis/serving_cn.md | 94 +- docs/reference/concepts/domain_cn.md | 4 +- docs/reference/concepts/domainroute_cn.md | 2 + docs/reference/concepts/kusciajob_cn.md | 4 +- .../reference/troubleshoot/custom_registry.md | 57 ++ docs/reference/troubleshoot/index.rst | 4 +- .../troubleshoot/k8s_ulimit_check.md | 18 + docs/tutorial/run_bfia_job_cn.md | 4 +- go.mod | 22 +- go.sum | 90 +- hack/errorcode/gen_error_code_doc.sh | 180 ++-- hack/k8s/autonomy/configmap.yaml | 1 - hack/k8s/master/configmap.yaml | 1 - hack/proto-to-go.sh | 5 + pkg/agent/pleg/generic.go | 4 +- pkg/agent/pleg/pleg.go | 2 +- pkg/agent/provider/pod/cri_provider.go | 6 +- pkg/agent/provider/pod/cri_provider_test.go | 150 +++ pkg/common/constants.go | 17 +- pkg/common/error.go | 48 + pkg/common/error_test.go | 41 + pkg/confmanager/bean/http_server_bean.go | 8 +- .../handler/grpchandler/configuration.go | 6 +- pkg/confmanager/service/certificate.go | 20 +- pkg/confmanager/service/certificate_test.go | 59 +- pkg/confmanager/service/configuration.go | 14 +- .../clusterdomainroute/controller.go | 4 +- .../clusterdomainroute/controller_test.go | 2 +- pkg/controllers/clusterdomainroute/domain.go | 2 +- .../clusterdomainroute/domainroute.go | 38 +- .../clusterdomainroute/domainroute_test.go | 150 +++ pkg/controllers/domainroute/check.go | 2 +- pkg/controllers/domainroute/controller.go | 2 +- pkg/controllers/kusciadeployment/reconcile.go | 2 +- pkg/controllers/kusciadeployment/util.go | 2 +- pkg/controllers/kusciajob/controller_test.go | 5 +- pkg/controllers/kusciajob/handler/approval.go | 6 +- .../kusciajob/handler/approval_test.go | 1 + .../kusciajob/handler/cancelled_test.go | 3 +- .../kusciajob/handler/failed_test.go | 3 +- .../kusciajob/handler/initialized.go | 5 +- .../kusciajob/handler/initialized_test.go | 1 + .../kusciajob/handler/pending_test.go | 1 + pkg/controllers/kusciajob/handler/running.go | 31 +- .../kusciajob/handler/running_test.go | 25 +- .../kusciajob/handler/scheduler.go | 115 ++- .../kusciajob/handler/scheduler_test.go | 8 + .../kusciajob/handler/succeeded_test.go | 3 +- .../kusciajob/handler/suspended_test.go | 3 +- pkg/controllers/kusciatask/controller.go | 2 +- pkg/controllers/kusciatask/controller_test.go | 9 + pkg/controllers/kusciatask/handler/common.go | 19 + .../kusciatask/handler/failed_handler.go | 16 +- .../kusciatask/handler/failed_handler_test.go | 5 +- .../kusciatask/handler/finished_handler.go | 2 +- .../handler/finished_handler_test.go | 1 + .../kusciatask/handler/pending_handler.go | 88 +- .../handler/pending_handler_test.go | 4 + .../kusciatask/handler/running_handler.go | 36 +- .../handler/running_handler_test.go | 10 +- .../handler/succeeded_handler_test.go | 5 +- pkg/controllers/options_test.go | 1 + pkg/controllers/server.go | 2 +- .../taskresourcegroup/controller.go | 2 +- .../taskresourcegroup/controller_test.go | 11 + .../handler/creating_handler.go | 13 +- .../handler/creating_handler_test.go | 31 +- .../taskresourcegroup/handler/factory_test.go | 3 + .../handler/failed_handler.go | 2 +- .../handler/failed_handler_test.go | 2 + .../handler/pending_handler.go | 20 - .../handler/pending_handler_test.go | 19 +- .../handler/reserve_failed_handler_test.go | 2 + .../handler/reserved_handler.go | 2 +- .../handler/reserved_handler_test.go | 2 + .../handler/reserving_handler_test.go | 2 + .../apis/kuscia/v1alpha1/domainroute_types.go | 5 +- .../apis/kuscia/v1alpha1/kusciajob_types.go | 7 + .../v1alpha1/taskresourcegroup_types.go | 2 +- .../kuscia/v1alpha1/zz_generated.deepcopy.go | 1 + pkg/datamesh/bean/grpc_server_bean.go | 20 +- pkg/datamesh/bean/http_server_bean.go | 23 +- pkg/datamesh/bean/operator_bean.go | 2 +- pkg/datamesh/commands/root_test.go | 198 ++++ .../{datamesh_config.go => dmconfig.go} | 11 + pkg/datamesh/config/dmconfig_test.go | 32 + pkg/datamesh/dmflight/action_handlers.go | 161 +++ .../dmflight/action_handlers_test.go} | 23 +- .../dmflight/builtin_dataio_direct.go | 47 + .../dmflight/builtin_dataio_direct_test.go | 42 + .../dmflight/builtin_dataio_localfile.go | 109 ++ .../dmflight/builtin_dataio_localfile_test.go | 252 +++++ pkg/datamesh/dmflight/builtin_dataio_oss.go | 152 +++ .../dmflight/builtin_dataio_oss_test.go | 274 +++++ pkg/datamesh/dmflight/context.go | 129 +++ pkg/datamesh/dmflight/context_test.go | 198 ++++ pkg/datamesh/dmflight/dataio.go | 150 +++ pkg/datamesh/dmflight/dataio_test.go | 206 ++++ pkg/datamesh/dmflight/datamesh_handler.go | 284 ++++++ .../dmflight/datamesh_handler_test.go | 458 +++++++++ pkg/datamesh/dmflight/oss_uploader.go | 288 ++++++ pkg/datamesh/dmflight/oss_uploader_test.go | 271 +++++ pkg/datamesh/dmflight/utils.go | 132 +++ pkg/datamesh/dmflight/utils_test.go | 153 +++ pkg/datamesh/errorcode/error_code.go | 45 +- pkg/datamesh/flight/dataproxy_client.go | 140 --- pkg/datamesh/flight/dataproxy_client_test.go | 78 -- pkg/datamesh/flight/example/mock_dataproxy.go | 346 ------- pkg/datamesh/flight/metaserver.go | 336 ------- pkg/datamesh/flight/metaserver_test.go | 231 ----- pkg/datamesh/flight/metautils.go | 54 - .../handler/grpchandler/flight_handler.go | 164 --- pkg/datamesh/service/domaindata.go | 26 +- pkg/datamesh/service/domaindatagrant.go | 37 +- pkg/datamesh/service/domaindatasource.go | 20 +- pkg/{common => datamesh/service}/merge_crd.go | 2 +- pkg/datamesh/v1handler/README.md | 0 .../grpchandler/raw_datamgr_handler.go} | 0 .../httphandler/domaindata/create.go | 2 +- .../httphandler/domaindata/delete.go | 2 +- .../httphandler/domaindata/query.go | 2 +- .../httphandler/domaindata/update.go | 2 +- .../httphandler/domaindatagrant/create.go | 2 +- .../httphandler/domaindatagrant/delete.go | 2 +- .../httphandler/domaindatagrant/query.go | 2 +- .../httphandler/domaindatagrant/update.go | 2 +- .../httphandler/domaindatasource/query.go | 2 +- .../embedstrings}/cgroup_pre_detect.sh | 0 .../embedstrings}/iptables_pre_detect.sh | 0 pkg/embedstrings/strings.go | 24 + pkg/gateway/controller/domain_route.go | 2 +- pkg/gateway/controller/handshake.go | 36 +- pkg/gateway/controller/register_node_test.go | 22 +- pkg/gateway/utils/transit_test.go | 1 - pkg/gateway/xds/xds.go | 2 +- pkg/interconn/kuscia/controller.go | 14 +- pkg/interconn/kuscia/controller_test.go | 6 + pkg/interconn/kuscia/deployment.go | 2 +- pkg/interconn/kuscia/deployment_test.go | 7 + pkg/interconn/kuscia/deploysummary.go | 2 +- pkg/interconn/kuscia/deploysummary_test.go | 3 + .../kuscia/hostresources/deployment.go | 2 +- .../kuscia/hostresources/deployment_test.go | 4 + .../kuscia/hostresources/deploymentsummary.go | 2 +- .../hostresources/deploymentsummary_test.go | 3 + .../kuscia/hostresources/domaindata.go | 2 +- .../kuscia/hostresources/domaindata_test.go | 5 + .../kuscia/hostresources/domaindatagrant.go | 2 +- .../hostresources/domaindatagrant_test.go | 5 + pkg/interconn/kuscia/hostresources/job.go | 2 +- .../kuscia/hostresources/job_test.go | 3 + .../kuscia/hostresources/jobsummary.go | 49 +- .../kuscia/hostresources/jobsummary_test.go | 6 +- pkg/interconn/kuscia/hostresources/manager.go | 17 + .../kuscia/hostresources/tasksummary.go | 2 +- .../kuscia/hostresources/tasksummary_test.go | 8 +- pkg/interconn/kuscia/interopconfig_test.go | 7 + pkg/interconn/kuscia/job.go | 157 ++- pkg/interconn/kuscia/job_test.go | 137 ++- pkg/interconn/kuscia/jobsummary.go | 39 +- pkg/interconn/kuscia/jobsummary_test.go | 7 +- pkg/interconn/kuscia/task.go | 108 +- pkg/interconn/kuscia/task_test.go | 68 +- pkg/interconn/kuscia/taskresource.go | 2 +- pkg/interconn/kuscia/taskresource_test.go | 3 + pkg/interconn/kuscia/tasksummary.go | 54 +- pkg/interconn/kuscia/tasksummary_test.go | 8 +- pkg/kusciaapi/bean/grpc_server_bean.go | 11 +- pkg/kusciaapi/bean/http_server_bean.go | 29 +- pkg/kusciaapi/config/kusciaapi_config.go | 2 + pkg/kusciaapi/errorcode/error_code.go | 117 +-- .../handler/grpchandler/certificate.go | 6 +- .../handler/grpchandler/domain_handler.go | 2 +- .../grpchandler/domaindata_grant_handler.go | 2 +- .../handler/grpchandler/domaindata_handler.go | 2 +- .../grpchandler/domaindata_source_handler.go | 2 +- .../handler/grpchandler/serving_handler.go | 2 +- .../handler/httphandler/domaindata/create.go | 2 +- .../handler/httphandler/domaindata/delete.go | 2 +- .../handler/httphandler/domaindata/query.go | 2 +- .../handler/httphandler/domaindata/update.go | 2 +- pkg/kusciaapi/service/apilite_service_test.go | 2 +- pkg/kusciaapi/service/domain_route_service.go | 35 +- .../service/domain_route_service_lite.go | 20 +- .../service/domain_route_service_test.go | 14 +- pkg/kusciaapi/service/domain_service.go | 142 ++- pkg/kusciaapi/service/domain_service_lite.go | 22 +- pkg/kusciaapi/service/domain_service_test.go | 71 +- pkg/kusciaapi/service/domaindata_grant.go | 31 +- .../service/domaindata_grant_test.go | 7 +- pkg/kusciaapi/service/domaindata_service.go | 73 +- .../service/domaindata_service_test.go | 2 +- pkg/kusciaapi/service/domaindata_source.go | 79 +- .../service/domaindata_source_test.go | 9 +- pkg/kusciaapi/service/health_service.go | 2 +- pkg/kusciaapi/service/job_service.go | 136 +-- pkg/kusciaapi/service/job_service_lite.go | 40 +- pkg/kusciaapi/service/job_service_test.go | 6 +- pkg/kusciaapi/service/serving_service.go | 54 +- pkg/transport/msq/config.go | 6 - pkg/transport/msq/mem_control.go | 7 +- pkg/transport/msq/session_manager.go | 24 +- pkg/transport/msq/session_manager_test.go | 159 ++- pkg/transport/msq/session_queue.go | 28 +- pkg/transport/msq/session_queue_test.go | 18 +- pkg/transport/msq/topic_queue.go | 4 +- pkg/transport/msq/topic_queue_test.go | 2 +- pkg/transport/server/grpc/server_test.go | 23 +- pkg/transport/server/http/server.go | 3 +- pkg/transport/server/http/server_test.go | 21 +- pkg/utils/network/grpc.go | 42 + pkg/utils/network/grpc_test.go | 26 + pkg/utils/network/port_alloc.go | 57 ++ pkg/utils/network/port_alloc_test.go | 42 + pkg/utils/resources/common.go | 8 +- pkg/utils/resources/common_test.go | 2 +- pkg/utils/resources/domaindata.go | 2 +- pkg/utils/resources/domaindatagrant.go | 2 +- pkg/utils/supervisor/supervisor.go | 101 +- pkg/utils/supervisor/supervisor_test.go | 7 +- pkg/web/constants/constants.go | 1 + pkg/web/decorator/proto_decorator.go | 11 + .../errorcode/{errorcode.go => error_code.go} | 8 +- pkg/web/framework/beans/gin_bean.go | 2 + pkg/web/interceptor/common.go | 72 ++ pkg/web/interceptor/grpc_interceptor.go | 158 +++ pkg/web/interceptor/http_interceptor.go | 114 +++ pkg/web/utils/desensitization.go | 170 ++++ pkg/web/utils/desensitization_test.go | 250 +++++ pkg/web/utils/response_status.go | 14 +- proto/api/v1alpha1/common.pb.go | 106 +- proto/api/v1alpha1/common.proto | 4 + proto/api/v1alpha1/datamesh/flightdm.pb.go | 950 ++---------------- proto/api/v1alpha1/datamesh/flightdm.proto | 57 +- proto/api/v1alpha1/datamesh/flightinner.pb.go | 312 ------ proto/api/v1alpha1/datamesh/flightinner.proto | 36 - proto/api/v1alpha1/errorcode/error_code.pb.go | 614 +++++++++++ proto/api/v1alpha1/errorcode/error_code.proto | 129 +++ python/README.md | 2 + python/kuscia/datamesh/__init__.py | 22 + python/kuscia/datamesh/api.py | 108 ++ python/kuscia/datamesh/config.py | 19 + python/kuscia/datamesh/datamanager.py | 61 ++ python/kuscia/datamesh/dataproxy.py | 158 +++ .../kuscia/proto/api/v1alpha1/common_pb2.py | 8 +- .../api/v1alpha1/datamesh/domaindata_pb2.py | 4 +- .../v1alpha1/datamesh/domaindatasource_pb2.py | 4 +- .../api/v1alpha1/datamesh/flightdm_pb2.py | 53 +- .../proto/api/v1alpha1/errorcode/__init__.py | 14 + .../api/v1alpha1/errorcode/error_code_pb2.py | 27 + .../v1alpha1/errorcode/error_code_pb2_grpc.py | 3 + .../v1alpha1/kusciaapi/domain_route_pb2.py | 88 +- .../kusciaapi/domaindatasource_pb2.py | 34 +- .../kusciaapi/domaindatasource_pb2_grpc.py | 33 + .../proto/api/v1alpha1/kusciaapi/job_pb2.py | 148 +-- .../api/v1alpha1/kusciaapi/job_pb2_grpc.py | 99 ++ python/requirements.txt | 4 +- python/test/test_datamesh.py | 145 +++ .../create_reverse_tunnel_test_cluster.sh | 4 +- scripts/deploy/deploy.sh | 8 +- scripts/deploy/import_engine_image.sh | 50 - scripts/deploy/init_kusciaapi_client_certs.sh | 6 +- scripts/deploy/kuscia.sh | 16 +- scripts/deploy/register_app_image.sh | 159 +++ scripts/deploy/start_standalone.sh | 6 +- scripts/templates/app_image.secretflow.yaml | 2 +- test/util/utils.go | 14 +- thirdparty/fate/cmd/job/job.go | 2 +- thirdparty/fate/cmd/main.go | 2 +- thirdparty/fate/cmd/modlules/modules.go | 2 +- .../scripts/templates/fate/hybrid_job.yaml | 1 + 334 files changed, 10342 insertions(+), 4906 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/Consulting_issue_template.yaml rename .github/ISSUE_TEMPLATE/{issue_template.yaml => Error_issue_template.yaml} (81%) create mode 100644 cmd/example/datamesh/README.md rename {pkg/datamesh/flight/example => cmd/example/datamesh}/arrdata.go (99%) create mode 100644 docs/imgs/flight_do_get.png create mode 100644 docs/imgs/flight_do_put.png create mode 100644 docs/reference/apis/datamesh/datacrud_cn.md create mode 100644 docs/reference/troubleshoot/custom_registry.md create mode 100644 docs/reference/troubleshoot/k8s_ulimit_check.md create mode 100644 pkg/agent/provider/pod/cri_provider_test.go create mode 100644 pkg/common/error.go create mode 100644 pkg/common/error_test.go create mode 100644 pkg/controllers/clusterdomainroute/domainroute_test.go create mode 100644 pkg/datamesh/commands/root_test.go rename pkg/datamesh/config/{datamesh_config.go => dmconfig.go} (90%) create mode 100644 pkg/datamesh/config/dmconfig_test.go create mode 100644 pkg/datamesh/dmflight/action_handlers.go rename pkg/{confmanager/errorcode/error_code.go => datamesh/dmflight/action_handlers_test.go} (56%) create mode 100644 pkg/datamesh/dmflight/builtin_dataio_direct.go create mode 100644 pkg/datamesh/dmflight/builtin_dataio_direct_test.go create mode 100644 pkg/datamesh/dmflight/builtin_dataio_localfile.go create mode 100644 pkg/datamesh/dmflight/builtin_dataio_localfile_test.go create mode 100644 pkg/datamesh/dmflight/builtin_dataio_oss.go create mode 100644 pkg/datamesh/dmflight/builtin_dataio_oss_test.go create mode 100644 pkg/datamesh/dmflight/context.go create mode 100644 pkg/datamesh/dmflight/context_test.go create mode 100644 pkg/datamesh/dmflight/dataio.go create mode 100644 pkg/datamesh/dmflight/dataio_test.go create mode 100644 pkg/datamesh/dmflight/datamesh_handler.go create mode 100644 pkg/datamesh/dmflight/datamesh_handler_test.go create mode 100644 pkg/datamesh/dmflight/oss_uploader.go create mode 100644 pkg/datamesh/dmflight/oss_uploader_test.go create mode 100644 pkg/datamesh/dmflight/utils.go create mode 100644 pkg/datamesh/dmflight/utils_test.go delete mode 100644 pkg/datamesh/flight/dataproxy_client.go delete mode 100644 pkg/datamesh/flight/dataproxy_client_test.go delete mode 100644 pkg/datamesh/flight/example/mock_dataproxy.go delete mode 100644 pkg/datamesh/flight/metaserver.go delete mode 100644 pkg/datamesh/flight/metaserver_test.go delete mode 100644 pkg/datamesh/flight/metautils.go delete mode 100644 pkg/datamesh/handler/grpchandler/flight_handler.go rename pkg/{common => datamesh/service}/merge_crd.go (99%) create mode 100644 pkg/datamesh/v1handler/README.md rename pkg/datamesh/{handler/grpchandler/simple_handler.go => v1handler/grpchandler/raw_datamgr_handler.go} (100%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindata/create.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindata/delete.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindata/query.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindata/update.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindatagrant/create.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindatagrant/delete.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindatagrant/query.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindatagrant/update.go (99%) rename pkg/datamesh/{handler => v1handler}/httphandler/domaindatasource/query.go (99%) rename {scripts/deploy => pkg/embedstrings}/cgroup_pre_detect.sh (100%) rename {scripts/deploy => pkg/embedstrings}/iptables_pre_detect.sh (100%) create mode 100644 pkg/embedstrings/strings.go create mode 100644 pkg/utils/network/grpc.go create mode 100644 pkg/utils/network/grpc_test.go create mode 100644 pkg/utils/network/port_alloc.go create mode 100644 pkg/utils/network/port_alloc_test.go rename pkg/web/errorcode/{errorcode.go => error_code.go} (81%) create mode 100644 pkg/web/utils/desensitization.go create mode 100644 pkg/web/utils/desensitization_test.go delete mode 100644 proto/api/v1alpha1/datamesh/flightinner.pb.go delete mode 100644 proto/api/v1alpha1/datamesh/flightinner.proto create mode 100644 proto/api/v1alpha1/errorcode/error_code.pb.go create mode 100644 proto/api/v1alpha1/errorcode/error_code.proto create mode 100644 python/kuscia/datamesh/__init__.py create mode 100644 python/kuscia/datamesh/api.py create mode 100644 python/kuscia/datamesh/config.py create mode 100644 python/kuscia/datamesh/datamanager.py create mode 100644 python/kuscia/datamesh/dataproxy.py create mode 100644 python/kuscia/proto/api/v1alpha1/errorcode/__init__.py create mode 100644 python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2.py create mode 100644 python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2_grpc.py create mode 100644 python/test/test_datamesh.py delete mode 100644 scripts/deploy/import_engine_image.sh create mode 100755 scripts/deploy/register_app_image.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 90b785e0..614064c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,12 +27,12 @@ parameters: executors: linux_x64_executor: # declares a reusable executor docker: - - image: secretflow/kuscia-ci:0.4 + - image: secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia-ci:0.5 resource_class: 2xlarge shell: /bin/bash --login -eo pipefail linux_aarch64_executor: docker: - - image: secretflow/ubuntu-base-ci:latest + - image: secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia-ci:0.5 resource_class: arm.2xlarge shell: /bin/bash --login -eo pipefail @@ -40,62 +40,25 @@ commands: test: steps: - checkout - - setup_remote_docker - - run: - name: "install go" - command: | - ARCH=$(uname -m) - case "$ARCH" in - x86_64) ARCH="amd64";; - aarch64) ARCH="arm64";; - *) echo "Unsupported architecture"; exit 1;; - esac - - GOLANG_DIR="/usr/local" - GOLANG_VERSION="1.19.7" - GOLANG_URL="https://golang.org/dl/go${GOLANG_VERSION}.linux-${ARCH}.tar.gz" - - if ! command -v go &> /dev/null; then - wget "$GOLANG_URL" - tar -C "$GOLANG_DIR" -xzf "go${GOLANG_VERSION}.linux-${ARCH}.tar.gz" - echo 'export PATH=$PATH:/usr/local/go/bin' | tee -a ~/.bashrc - echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV - source ~/.bashrc - fi - run: - name: Test Kuscia + name: "kuscia test" command: make test build_kuscia: steps: - - setup_remote_docker - - run: - name: "install go" - command: | - ARCH=$(uname -m) - case "$ARCH" in - x86_64) ARCH="amd64";; - aarch64) ARCH="arm64";; - *) echo "Unsupported architecture"; exit 1;; - esac - - GOLANG_DIR="/usr/local" - GOLANG_VERSION="1.19.7" - GOLANG_URL="https://golang.org/dl/go${GOLANG_VERSION}.linux-${ARCH}.tar.gz" - - if ! command -v go &> /dev/null; then - wget "$GOLANG_URL" - tar -C "$GOLANG_DIR" -xzf "go${GOLANG_VERSION}.linux-${ARCH}.tar.gz" - echo 'export PATH=$PATH:/usr/local/go/bin' | tee -a ~/.bashrc - echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV - source ~/.bashrc - fi + - checkout - run: name: "make build" command: | git config --global --add safe.directory ./ make build - - store_artifacts: - path: build + + DIR="/tmp/build/linux" + mkdir -p $DIR + cp -rf ./build/linux/* $DIR + - persist_to_workspace: + root: /tmp/build + paths: + - linux # Define a job to be invoked later in a workflow. # See: https://circleci.com/docs/configuration-reference/#jobs jobs: @@ -117,19 +80,21 @@ jobs: - build_kuscia image_publish: docker: - - image: secretflow/kuscia-ci:0.4 + - image: cimg/deploy:2023.06.1 shell: /bin/bash --login -eo pipefail steps: - - attach_workspace: - at: build - checkout - setup_remote_docker + - attach_workspace: + at: /tmp/build - run: name: Build image and publish command: | + set -ex + mv /tmp/build/linux ./build/ CIRCLETAG=$(echo ${CIRCLE_TAG} | sed 's/v//') - IMAGE = "secretflow/kuscia" - ALIYUN_IMAGE = "secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia" + IMAGE="secretflow/kuscia" + ALIYUN_IMAGE="secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia" # login kuscia dockerhub registry docker login -u secretflow -p ${DOCKER_DEPLOY_TOKEN} @@ -137,17 +102,19 @@ jobs: docker buildx create --name kuscia --platform linux/arm64,linux/amd64 --use docker buildx build -t ${IMAGE}:${CIRCLETAG} --platform linux/arm64,linux/amd64 -f ./build/dockerfile/kuscia-anolis.Dockerfile . --push + #docker buildx build -t ${IMAGE}:latest --platform linux/arm64,linux/amd64 -f ./build/dockerfile/kuscia-anolis.Dockerfile . --push # login kuscia aliyun registry docker login -u ${ALIYUN_DOCKER_USERNAME} -p ${ALIYUN_DOCKER_PASSWORD} secretflow-registry.cn-hangzhou.cr.aliyuncs.com + #docker buildx build -t ${ALIYUN_IMAGE}:latest --platform linux/arm64,linux/amd64 -f ./build/dockerfile/kuscia-anolis.Dockerfile . --push docker buildx build -t ${ALIYUN_IMAGE}:${CIRCLETAG} --platform linux/arm64,linux/amd64 -f ./build/dockerfile/kuscia-anolis.Dockerfile . --push # Orchestrate jobs using workflows # See: https://circleci.com/docs/configuration-reference/#workflows workflows: - unit-test: + kuscia-workflow: when: and: - not: << pipeline.parameters.GHA_Action >> @@ -157,18 +124,15 @@ workflows: matrix: parameters: executor: [ "linux_x64_executor", "linux_aarch64_executor" ] - build-workflow: - when: - not: - or: - - << pipeline.parameters.GHA_Action >> - - equal: [ "publish_kuscia_deps", << pipeline.parameters.GHA_Meta >> ] - - equal: [ "publish_pypi", << pipeline.parameters.GHA_Meta >> ] - jobs: - linux_build_kuscia: matrix: parameters: executor: [ "linux_x64_executor", "linux_aarch64_executor" ] + filters: + branches: + ignore: /.*/ + tags: + only: /^v.*/ - image_publish: requires: - linux_build_kuscia diff --git a/.circleci/deps-config.yml b/.circleci/deps-config.yml index 49c789de..411a9b13 100644 --- a/.circleci/deps-config.yml +++ b/.circleci/deps-config.yml @@ -22,12 +22,12 @@ parameters: executors: linux_x64_executor: # declares a reusable executor docker: - - image: secretflow/kuscia-ci:0.4 + - image: secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia-ci:0.5 resource_class: 2xlarge shell: /bin/bash --login -eo pipefail linux_aarch64_executor: docker: - - image: secretflow/ubuntu-base-ci:latest + - image: secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia-ci:0.5 resource_class: arm.2xlarge shell: /bin/bash --login -eo pipefail @@ -36,28 +36,6 @@ commands: steps: - setup_remote_docker: docker_layer_caching: true - - run: - name: "install go" - command: | - ARCH=$(uname -m) - case "$ARCH" in - x86_64) ARCH="amd64";; - aarch64) ARCH="arm64";; - *) echo "Unsupported architecture"; exit 1;; - esac - - GOLANG_DIR="/opt" - GOLANG_VERSION="1.19.7" - GOLANG_URL="https://golang.org/dl/go${GOLANG_VERSION}.linux-${ARCH}.tar.gz" - - if ! command -v go &> /dev/null; then - wget "$GOLANG_URL" - - tar -C "$GOLANG_DIR" -xzf "go${GOLANG_VERSION}.linux-${ARCH}.tar.gz" - echo 'export PATH=$PATH:/opt/go/bin' | tee -a ~/.bashrc - echo 'export PATH=$PATH:/opt/go/bin' >> $BASH_ENV - source ~/.bashrc - fi - run: name: "make build" command: | @@ -78,7 +56,7 @@ jobs: - build_kuscia_deps image_publish: docker: - - image: secretflow/kuscia-ci:0.4 + - image: cimg/deploy:2023.06.1 shell: /bin/bash --login -eo pipefail steps: - attach_workspace: @@ -88,7 +66,7 @@ jobs: - run: name: Build image and publish command: | - DEPS_IMAGE = secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia-deps:<< pipeline.parameters.DEPS_Tag >> + DEPS_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia-deps:<< pipeline.parameters.DEPS_Tag >> # login kuscia aliyun registry docker login -u ${ALIYUN_DOCKER_USERNAME} -p ${ALIYUN_DOCKER_PASSWORD} secretflow-registry.cn-hangzhou.cr.aliyuncs.com @@ -107,4 +85,4 @@ workflows: executor: [ "linux_x64_executor", "linux_aarch64_executor" ] - image_publish: requires: - - linux_build_kuscia_deps + - linux_build_kuscia_deps \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/Consulting_issue_template.yaml b/.github/ISSUE_TEMPLATE/Consulting_issue_template.yaml new file mode 100644 index 00000000..2a1f32f1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Consulting_issue_template.yaml @@ -0,0 +1,57 @@ +name: Consulting issue template +description: Thank you for reporting the issue! +body: + - type: markdown + attributes: + value: | + Please ensure that you are reporting the consultation issue on GitHub.(Api Usage/Feature/Document/Others) + Please post on our [discussions](https://github.com/secretflow/kuscia/discussions) instead if you want to ask questions or share ideas. + - type: dropdown + id: issue-type + attributes: + label: Issue Type + description: What type of issue would you like to report? + multiple: false + options: + - Api Usage + - Feature + - Document + - Others + validations: + required: true + - type: dropdown + id: searched-for-existing-issues + attributes: + label: Search for existing issues similar to yours + description: Existing [documents](https://www.secretflow.org.cn/zh-CN/docs/kuscia/main/) and [issues](https://github.com/secretflow/kuscia/issues) + options: + - 'Yes' + - 'No' + validations: + required: true + - type: input + id: kuscia-version + attributes: + label: Kuscia Version + description: + placeholder: e.g., kuscia 0.7.0b0 + validations: + required: true + - type: input + id: link + attributes: + label: Link to Relevant Documentation + description: For faster problem-solving, if there are relevant documents, please attach links. + placeholder: e.g.,https://www.secretflow.org.cn/zh-CN/docs/kuscia/main/reference/concepts + validations: + required: false + - type: textarea + id: Question-Details + attributes: + label: Question Details + description: Please provide a detailed description of the problem you have encountered, including the performance of the problem, the difference between expected and actual behavior, and the solutions you have tried. This will help us understand and solve your problem faster. + placeholder: Describe the questions you want to consult and what you want to do + value: + render: shell + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/issue_template.yaml b/.github/ISSUE_TEMPLATE/Error_issue_template.yaml similarity index 81% rename from .github/ISSUE_TEMPLATE/issue_template.yaml rename to .github/ISSUE_TEMPLATE/Error_issue_template.yaml index 8ec6a746..503a481b 100644 --- a/.github/ISSUE_TEMPLATE/issue_template.yaml +++ b/.github/ISSUE_TEMPLATE/Error_issue_template.yaml @@ -1,10 +1,10 @@ -name: Kuscia Issue Template +name: Error Issue Template description: Thank you for reporting the issue! body: - type: markdown attributes: value: | - Please make sure that you report a code/doc bug, feature request or build/installation bug on GitHub. + Please ensure that you are reporting the consultation issue on GitHub.(Install/Deploy、Running、Document、Other) Please post on our [discussions](https://github.com/secretflow/kuscia/discussions) instead if you want to ask questions or share ideas. - type: dropdown id: issue-type @@ -13,56 +13,56 @@ body: description: What type of issue would you like to report? multiple: false options: - - Bug - - Build/Install - - Feature Request - - Documentation Feature Request - - Documentation Bug + - Install/Deploy + - Running + - Document - Others validations: required: true - type: dropdown - id: deployment + id: searched-for-existing-issues attributes: - label: Deployment - description: Kuscia Deployed by + label: Search for existing issues similar to yours + description: Existing [documents](https://www.secretflow.org.cn/zh-CN/docs/kuscia/main/) and [issues](https://github.com/secretflow/kuscia/issues) options: - - docker - - k8s + - 'Yes' + - 'No' validations: required: true - type: input - id: kuscia-version + id: OS attributes: - label: Kuscia Version + label: OS Platform and Distribution description: - placeholder: e.g., kuscia 0.7.0b0 + placeholder: e.g., Linux Ubuntu 18.04 validations: required: true - type: input - id: OS + id: kuscia-version attributes: - label: OS Platform and Distribution + label: Kuscia Version description: - placeholder: e.g., Linux Ubuntu 18.04 + placeholder: e.g., kuscia 0.7.0b0 validations: required: true - - type: input - id: docker-version + - type: dropdown + id: deployment attributes: - label: Docker version - description: e.g., Docker 20.10.8 - placeholder: e.g., Docker 20.10.8 + label: Deployment + description: Kuscia Deployed by + options: + - docker + - k8s validations: - required: false + required: true - type: input - id: k8s-version + id: deployment-version attributes: - label: K8s version + label: deployment Version description: - placeholder: e.g., 1.26.11 + placeholder: docker/k8s version validations: - required: false + required: true - type: dropdown id: running-app-type attributes: @@ -128,4 +128,4 @@ body: value: render: shell validations: - required: true \ No newline at end of file + required: true diff --git a/CHANGELOG.md b/CHANGELOG.md index b06080c9..edc7828f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `Security` in case of vulnerabilities. `Breaking Changed` Breaking for backward-incompatible changes that require user intervention. +## [v0.10.0.dev240531] - 2024-05-31 + +### Added +[Feature] domainRoute now supports setting prefix paths +[Feature] kuscia job now supports multiple parties (1 party , 2 parties , 3 parties) +[Feature] appImage's mountPath now supports relative paths +[Feature] Support for setting DomainRoute reverse tunnel mode through the Kuscia API +[Feature] Support for PKCS 8 format certificates +[Feature] Added DataSource List API to KusciaAPI. KusciaAPI Master side supports Query and List DataSource but does not display sensitive information. + +### Changed +[Documentation] Correct description of monitoring metrics in the Kuscia metric documentation +[scripts] During kuscia init, check that the node ID cannot be master +[Feature] Remove the trailing "/" from the RelatedURI field when creating domainData via KusciaAPI +[Feature] Added heartbeat time check to domainRoute, setting it to not ready if it exceeds the threshold +[Feature] Moved error code definitions in the code to Protobuf + +### Breaking Changed +[NA] + +### Fixed +[Bugfix] Fixed the issue of port conflicts in runp serving multi-replica +[Bugfix] Fixed the issue of state anomalies being caused by multiple executions of kusciaJob stop and restart +[Unit Test] Improved the stability and concurrency of unit tests + ## [v0.9.0.dev240430] - 2024-04-30 ### Added diff --git a/Makefile b/Makefile index 1a20a0bf..491471ef 100644 --- a/Makefile +++ b/Makefile @@ -77,11 +77,11 @@ vet: ## Run go vet against code. .PHONY: verify_error_code verify_error_code: ## Verify integrity of error code i18n configuration. - bash hack/errorcode/gen_error_code_doc.sh verify pkg/kusciaapi/errorcode/error_code.go hack/errorcode/i18n/errorcode.zh-CN.toml + bash hack/errorcode/gen_error_code_doc.sh verify proto/api/v1alpha1/errorcode/error_code.proto hack/errorcode/i18n/errorcode.zh-CN.toml .PHONY: gen_error_code_doc gen_error_code_doc: verify_error_code ## Generate error code markdown doc. - bash hack/errorcode/gen_error_code_doc.sh doc pkg/kusciaapi/errorcode/error_code.go hack/errorcode/i18n/errorcode.zh-CN.toml docs/reference/apis/error_code_cn.md + bash hack/errorcode/gen_error_code_doc.sh doc proto/api/v1alpha1/errorcode/error_code.proto hack/errorcode/i18n/errorcode.zh-CN.toml docs/reference/apis/error_code_cn.md .PHONY: check_code check_code: verify_error_code fmt vet ## check code format diff --git a/build/dockerfile/kuscia-secretflow.Dockerfile b/build/dockerfile/kuscia-secretflow.Dockerfile index 24c7b040..f88ed76b 100644 --- a/build/dockerfile/kuscia-secretflow.Dockerfile +++ b/build/dockerfile/kuscia-secretflow.Dockerfile @@ -1,4 +1,4 @@ -ARG KUSCIA_IMAGE="secretflow/kuscia:0.9.0b0" +ARG KUSCIA_IMAGE="secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.10.0b0" FROM secretflow/anolis8-python:3.10.13 as python @@ -12,11 +12,11 @@ RUN yum install -y protobuf libnl3 libgomp && \ grep -rl '#!/root/miniconda3/envs/secretflow/bin' /usr/local/bin/ | xargs sed -i -e 's/#!\/root\/miniconda3\/envs\/secretflow/#!\/usr\/local/g' && \ rm /usr/local/bin/openssl -ARG SF_VERSION="1.7.0b0" +ARG SF_VERSION="1.8.0b0" RUN pip install secretflow-lite==${SF_VERSION} --extra-index-url https://mirrors.aliyun.com/pypi/simple/ && rm -rf /root/.cache RUN kuscia image builtin secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:${SF_VERSION} --store /home/kuscia/var/images WORKDIR /home/kuscia -ENTRYPOINT ["tini", "--"] +ENTRYPOINT ["tini", "--"] \ No newline at end of file diff --git a/cmd/example/datamesh/README.md b/cmd/example/datamesh/README.md new file mode 100644 index 00000000..6d30adf8 --- /dev/null +++ b/cmd/example/datamesh/README.md @@ -0,0 +1,36 @@ + + +## datamesh测试工具 + + +### 如何编译 + +编译命令 +``` +go build -ldflags="-s -w" -o build/apps/datamesh/datamesh ./cmd/example/datamesh +``` + + +### 如何测试 + +#### 启动DataMesh测试服务使用如下的命令 +``` +./datamesh --startDataMesh --log.level DEBUG +``` +注: 对外支持的数据源是默认的`localfs`数据源, 文件目录是 `var/storage/data` + + +如果要测试OSS数据源,可以增加OSS相关的配置(具体的参数意义可以使用 `./datamesh --help` 查看) +``` +./datamesh --startDataMesh --log.level DEBUG \ + --ossDataSource --ossEndpoint \ + --ossAccessKey \ + --ossAccessSecret \ + --ossBucket \ + --ossPrefix \ + --ossType +``` + + +#### 测试客户端 +建议使用 `python` 封装好的 `kuscia-sdk` 来进行测试, 参考文件 `/python/test/test_datamesh.py` diff --git a/pkg/datamesh/flight/example/arrdata.go b/cmd/example/datamesh/arrdata.go similarity index 99% rename from pkg/datamesh/flight/example/arrdata.go rename to cmd/example/datamesh/arrdata.go index 462d64e5..9604b661 100644 --- a/pkg/datamesh/flight/example/arrdata.go +++ b/cmd/example/datamesh/arrdata.go @@ -14,7 +14,7 @@ // this file is copy from https://github.com/apache/arrow -package example +package main import ( "fmt" diff --git a/cmd/example/datamesh/dm_client.go b/cmd/example/datamesh/dm_client.go index 93b86e43..773b4814 100644 --- a/cmd/example/datamesh/dm_client.go +++ b/cmd/example/datamesh/dm_client.go @@ -41,8 +41,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/secretflow/kuscia/pkg/common" - flight2 "github.com/secretflow/kuscia/pkg/datamesh/flight" - "github.com/secretflow/kuscia/pkg/datamesh/flight/example" + "github.com/secretflow/kuscia/pkg/datamesh/dmflight" "github.com/secretflow/kuscia/pkg/kusciaapi/service" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/paths" @@ -83,7 +82,7 @@ func (m *MockFlightClient) start(config *CertsConfig) error { mysqlDatasourceID string ) - m.flightClient, err = createFlightClient(metaServerEndpoint, config) + m.flightClient, _ = createFlightClient(metaServerEndpoint, config) if localDatasourceID, err = m.createLocalFsDataSource(); err != nil { return err } @@ -188,15 +187,14 @@ func (m *MockFlightClient) createDataSource(createDatasourceReq *kusciaapi.Creat nlog.Warn(err) return "", err } - nlog.Infof("data-source-id:%s, type is:%s", resp.Data.DatasourceId, createDatasourceReq.Type) + nlog.Infof("Data-source-id:%s, type is:%s", resp.Data.DatasourceId, createDatasourceReq.Type) return resp.Data.DatasourceId, nil } func (m *MockFlightClient) QueryDataSource(datasourceID string) error { - queryDatasourceReq := &datamesh.ActionQueryDomainDataSourceRequest{ - Request: &datamesh.QueryDomainDataSourceRequest{ - DatasourceId: datasourceID, - }, + queryDatasourceReq := &datamesh.QueryDomainDataSourceRequest{ + + DatasourceId: datasourceID, } msg, err := proto.Marshal(queryDatasourceReq) if err != nil { @@ -217,34 +215,32 @@ func (m *MockFlightClient) QueryDataSource(datasourceID string) error { return err } - var queryDatasourceResp datamesh.ActionQueryDomainDataSourceResponse + var queryDatasourceResp datamesh.QueryDomainDataSourceResponse if err := proto.Unmarshal(result.Body, &queryDatasourceResp); err != nil { nlog.Warnf("Unmarshal ActionQueryDomainDataSourceResponse err: %v", err) return err } - dsResp := queryDatasourceResp.Response.Data - nlog.Infof("dsType is %s, dsInfo:%v", dsResp.Type, dsResp.Info) + dsResp := queryDatasourceResp.Data + nlog.Infof("DsType is %s, dsInfo:%v", dsResp.Type, dsResp.Info) return nil } func (m *MockFlightClient) createDomainData(datasourceID string) (string, error) { - cols, err := common.ArrowSchema2DataMeshColumns(example.Records[m.testDataType][0].Schema()) + cols, err := common.ArrowSchema2DataMeshColumns(Records[m.testDataType][0].Schema()) nlog.Infof("DomainData Cols:%v", cols) if err != nil { return "", err } - createDomainDataReq := &datamesh.ActionCreateDomainDataRequest{ - Request: &datamesh.CreateDomainDataRequest{ - Header: nil, - DomaindataId: fmt.Sprintf("%s-%d", m.testDataType, time.Now().UnixNano()), - Name: m.testDataType, - Type: "table", - RelativeUri: "b.csv", // for mysql table, RelativeUri should be "dbName.tableName" - DatasourceId: datasourceID, - Columns: cols, - FileFormat: v1alpha1.FileFormat_CSV, - }, + createDomainDataReq := &datamesh.CreateDomainDataRequest{ + Header: nil, + DomaindataId: fmt.Sprintf("%s-%d", m.testDataType, time.Now().UnixNano()), + Name: m.testDataType, + Type: "table", + RelativeUri: "b.csv", // for mysql table, RelativeUri should be "dbName.tableName" + DatasourceId: datasourceID, + Columns: cols, + FileFormat: v1alpha1.FileFormat_CSV, } msg, err := proto.Marshal(createDomainDataReq) if err != nil { @@ -266,12 +262,12 @@ func (m *MockFlightClient) createDomainData(datasourceID string) (string, error) nlog.Warnf("Recv ActionCreateDomainDataResponse err: %v", err) } - var createDomainDataResp datamesh.ActionCreateDomainDataResponse + var createDomainDataResp datamesh.CreateDomainDataResponse if err := proto.Unmarshal(result.Body, &createDomainDataResp); err != nil { nlog.Warnf("Unmarshal ActionCreateDomainDataResponse err: %v", err) return "", err } - domainDataID := createDomainDataResp.Response.Data.DomaindataId + domainDataID := createDomainDataResp.Data.DomaindataId return domainDataID, nil } @@ -283,7 +279,7 @@ func (m *MockFlightClient) getData(domainDataID string, useRawData bool) error { if useRawData { cmd.ContentType = datamesh.ContentType_RAW } - desc, err := flight2.DescForCommand(cmd) + desc, err := dmflight.DescForCommand(cmd) if err != nil { nlog.Warnf(err.Error()) return err @@ -335,7 +331,7 @@ func (m *MockFlightClient) putData(domainDataID string) error { }, }, } - desc, err := flight2.DescForCommand(cmd) + desc, err := dmflight.DescForCommand(cmd) if err != nil { nlog.Warnf(err.Error()) return err @@ -348,16 +344,8 @@ func (m *MockFlightClient) putData(domainDataID string) error { } dpURI := flightInfo.Endpoint[0].Location[0].Uri - ticket, err := flight2.GetTicketDomainDataQuery(flightInfo.Endpoint[0].Ticket) - if err != nil { - nlog.Warnf("GetTicketDomainDataQuery fail:%v", err) - } - ticketID := ticket.DomaindataHandle - - putDesc, err := flight2.DescForCommand(ticket) - if err != nil { - nlog.Warnf("Generate put desc fail:%v", err) - } + ticket := flightInfo.Endpoint[0].Ticket + ticketID := ticket.GetTicket() flightClient, err := createFlightClient(dpURI, nil) if err != nil { @@ -368,10 +356,10 @@ func (m *MockFlightClient) putData(domainDataID string) error { var records []arrow.Record if m.testDataType == binaryTestData { nlog.Infof("MakeBinaryRecords ") - records = example.MakeBinaryRecords() + records = MakeBinaryRecords() } else { nlog.Infof("MakePrimitiveRecords ") - records = example.MakePrimitiveRecords() + records = MakePrimitiveRecords() } stream, err := flightClient.DoPut(context.Background()) @@ -380,30 +368,36 @@ func (m *MockFlightClient) putData(domainDataID string) error { return err } + if err := stream.SendMsg(&flight.Ticket{ + Ticket: ticketID, + }); err != nil { + nlog.Warnf("Send tick to stream Fail:%v", err) + return err + } + wr := flight.NewRecordWriter(stream, ipc.WithSchema(records[0].Schema())) - wr.SetFlightDescriptor(putDesc) defer wr.Close() for idx, r := range records { if err := wr.WriteWithAppMetadata(r, []byte(fmt.Sprintf("%d_%s", idx, ticketID)) /*metadata*/); err != nil { - nlog.Warnf("write data to data proxy fail:%v", err) + nlog.Warnf("Write data to data proxy fail:%v", err) return err } } pr, err := stream.Recv() if err != nil { - nlog.Warnf("recv error when write data to data proxy:%v", err) + nlog.Warnf("Recv error when write data to data proxy:%v", err) return err } acked := pr.GetAppMetadata() if len(acked) > 0 { - nlog.Infof("got metadata:%s", string(acked)) + nlog.Infof("Got metadata:%s", string(acked)) } stream.CloseSend() - nlog.Infof("put data succ") + nlog.Infof("Put data succ") return nil } @@ -424,7 +418,7 @@ func writeStructureCSV(writer io.Writer, r *flight.Reader) error { idx++ } w.Flush() - nlog.Infof("client get data succ") + nlog.Infof("Client get data succ") return nil } @@ -454,7 +448,7 @@ func createFlightClient(endpoint string, config *CertsConfig) (flight.Client, er if len(uriSlice) == 2 { endpoint = uriSlice[1] } - nlog.Infof("endpoint is %s", endpoint) + nlog.Infof("Endpoint is %s", endpoint) dialOpts := []grpc.DialOption{ grpc.WithKeepaliveParams(keepalive.ClientParameters{ @@ -476,7 +470,7 @@ func createFlightClient(endpoint string, config *CertsConfig) (flight.Client, er conn, err := grpc.Dial(endpoint, dialOpts...) if err != nil { - nlog.Errorf("create grpc conn to %s fail: %v", endpoint, err) + nlog.Errorf("Create grpc conn to %s fail: %v", endpoint, err) return nil, err } return flight.NewClientFromConn(conn, nil), nil @@ -492,16 +486,16 @@ func createClientCertificate() (*CertsConfig, error) { signingKeyFile := filepath.Join(certsDir, "ca.key") if err := paths.EnsureDirectory(certsDir, true); err != nil { - nlog.Warnf("cannot create certs dir: %v", err) + nlog.Warnf("Cannot create certs dir: %v", err) } if err := tls.CreateCAFile("testca", signingCertFile, signingKeyFile); err != nil { - nlog.Warnf("create ca file fail: %v", err) + nlog.Warnf("Create ca file fail: %v", err) return nil, err } DomainCACert, err := tls.ParseCert(nil, signingCertFile) if err != nil { - nlog.Warnf("parser domainCaCert fail:%v, err", err) + nlog.Warnf("Parser domainCaCert fail:%v, err", err) return nil, err } diff --git a/cmd/example/datamesh/dm_server.go b/cmd/example/datamesh/dm_server.go index 78ed8b3f..ada795a3 100644 --- a/cmd/example/datamesh/dm_server.go +++ b/cmd/example/datamesh/dm_server.go @@ -18,6 +18,7 @@ import ( "context" "crypto/rand" "crypto/rsa" + "encoding/json" "fmt" "os" "sync" @@ -25,20 +26,24 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/secretflow/kuscia/pkg/common" cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" + "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" "github.com/secretflow/kuscia/pkg/datamesh/commands" "github.com/secretflow/kuscia/pkg/datamesh/config" - "github.com/secretflow/kuscia/pkg/datamesh/flight/example" kusciaapiconfig "github.com/secretflow/kuscia/pkg/kusciaapi/config" "github.com/secretflow/kuscia/pkg/kusciaapi/service" "github.com/secretflow/kuscia/pkg/secretbackend" "github.com/secretflow/kuscia/pkg/utils/meta" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/nlog/zlogwriter" + "github.com/secretflow/kuscia/pkg/utils/paths" "github.com/secretflow/kuscia/pkg/utils/tls" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" ) const ( @@ -50,6 +55,7 @@ const ( ) type opts struct { + listenAddr string dataProxyEndpoint string startClient bool startDataMesh bool @@ -57,19 +63,36 @@ type opts struct { testDataType string outputCSVFilePath string logCfg *nlog.LogConfig + // oss data source + ossDataSourceID string + ossEndpoint string + ossAccessKey string + ossAccessSecret string + ossBucket string + ossPrefix string + ossType string } func (o *opts) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&o.listenAddr, "listenAddr", "0.0.0.0", "datamesh bind address") fs.StringVar(&o.dataProxyEndpoint, "dataProxyEndpoint", mockDataProxyEndpoint, "data proxy endpoint") - fs.BoolVar(&o.startClient, "startClient", true, "startClient") - fs.BoolVar(&o.startDataMesh, "startDataMesh", true, "startDataMesh") + fs.BoolVar(&o.startClient, "startClient", false, "startTestClient") + fs.BoolVar(&o.startDataMesh, "startDataMesh", false, "startDataMeshServer") fs.BoolVar(&o.enableDataMeshTLS, "enableDataMeshTLS", false, "enableDataMeshTLS") fs.StringVar(&o.testDataType, "testDataType", primitivesTestData, "binary or primitives,"+ "binary refers to schema [{binary,nullable}], primitives refers to [{bool,nullable},{int64,nullable},{float64,"+ "nullable}]") - fs.StringVar(&o.outputCSVFilePath, "outputCSVFilePath", "./a.csv", - "outputCSVFilePath") + fs.StringVar(&o.outputCSVFilePath, "outputCSVFilePath", "./a.csv", "outputCSVFilePath") + + fs.StringVar(&o.ossDataSourceID, "ossDataSource", "", "oss data source id") + fs.StringVar(&o.ossEndpoint, "ossEndpoint", "127.0.0.1:9000", "oss endpoint") + fs.StringVar(&o.ossAccessKey, "ossAccessKey", "", "oss access key") + fs.StringVar(&o.ossAccessSecret, "ossAccessSecret", "", "oss access secret") + fs.StringVar(&o.ossBucket, "ossBucket", "", "oss bucket") + fs.StringVar(&o.ossPrefix, "ossPrefix", "/", "oss path prefix") + fs.StringVar(&o.ossType, "ossType", "oss", "minio/oss") + o.logCfg = zlogwriter.InstallPFlags(fs) } @@ -98,40 +121,26 @@ func newCommand(ctx context.Context, o *opts) *cobra.Command { certsConfig, err := createClientCertificate() if err != nil { - nlog.Fatalf("create cert file fail :%v", err) - } + return fmt.Errorf("create cert file fail :%v", err) - conf := config.NewDefaultDataMeshConfig() - conf.ExternalDataProxyList = []config.ExternalDataProxyConfig{ - { - Endpoint: o.dataProxyEndpoint, - }, } + conf := config.NewDefaultDataMeshConfig() conf.KubeNamespace = mockDomain conf.KusciaClient = kusciafake.NewSimpleClientset() - conf.DisableTLS = !o.enableDataMeshTLS + conf.ListenAddr = o.listenAddr - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - nlog.Fatal("generate domainKey fail") + if err := loadConfigFromCmdEnv(conf, certsConfig, o); err != nil { + nlog.Fatal(err.Error()) return nil } - conf.DomainKey = privateKey - - if conf.TLS.ServerKey, err = tls.ParseKey([]byte{}, certsConfig.serverKeyFile); err != nil { - return err - } - if conf.TLS.ServerCert, err = tls.ParseCert([]byte{}, certsConfig.serverCertFile); err != nil { - return err - } - if conf.TLS.RootCA, err = tls.ParseCert([]byte{}, certsConfig.caFile); err != nil { - return err - } - if conf.DomainKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil { - nlog.Errorf("generate DomainKey fail:%s", err.Error()) + if err := setupOssDataSource(ctx, conf, o); err != nil { + nlog.Fatal(err.Error()) return nil } + + paths.EnsureDirectory(common.DefaultDomainDataSourceLocalFSPath, true) + runCtx, cancel := context.WithCancel(ctx) defer func() { cancel() @@ -150,8 +159,8 @@ func newCommand(ctx context.Context, o *opts) *cobra.Command { go startClient(cancel, o, certsConfig, conf) } + wg.Add(1) go func() { - wg.Add(1) defer wg.Done() if o.startDataMesh { if err := commands.Run(runCtx, conf, conf.KusciaClient); err != nil { @@ -173,10 +182,8 @@ func newCommand(ctx context.Context, o *opts) *cobra.Command { func startMockDataProxy(cancel context.CancelFunc, o *opts) { fmt.Println(o.dataProxyEndpoint) - dp := example.NewMockDataProxy(o.dataProxyEndpoint) - if err := dp.Start(); err != nil { - cancel() - } + time.Sleep(1000 * time.Second) + cancel() } func startClient(cancel context.CancelFunc, o *opts, certConfig *CertsConfig, dmConfig *config.DataMeshConfig) { @@ -211,3 +218,67 @@ func makeMemConfigurationService() cmservice.IConfigurationService { ) return configurationService } + +func loadConfigFromCmdEnv(conf *config.DataMeshConfig, certsConfig *CertsConfig, o *opts) error { + conf.DisableTLS = !o.enableDataMeshTLS + conf.ExternalDataProxyList = []config.ExternalDataProxyConfig{ + { + Endpoint: o.dataProxyEndpoint, + }, + } + + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return fmt.Errorf("generate domainKey fail") + } + conf.DomainKey = privateKey + + if conf.TLS.ServerKey, err = tls.ParseKey([]byte{}, certsConfig.serverKeyFile); err != nil { + return err + } + if conf.TLS.ServerCert, err = tls.ParseCert([]byte{}, certsConfig.serverCertFile); err != nil { + return err + } + if conf.TLS.RootCA, err = tls.ParseCert([]byte{}, certsConfig.caFile); err != nil { + return err + } + + if conf.DomainKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil { + return fmt.Errorf("generate DomainKey fail:%s", err.Error()) + } + return nil +} + +func setupOssDataSource(ctx context.Context, conf *config.DataMeshConfig, o *opts) error { + if o.ossDataSourceID != "" && o.ossBucket != "" { + ossConfig, _ := json.Marshal(&datamesh.DataSourceInfo{ + Oss: &datamesh.OssDataSourceInfo{ + Endpoint: o.ossEndpoint, + AccessKeyId: o.ossAccessKey, + AccessKeySecret: o.ossAccessSecret, + Bucket: o.ossBucket, + Prefix: o.ossPrefix, + StorageType: o.ossType, + Version: "s3v4", + Virtualhost: false, + }}) + + strConfig, _ := tls.EncryptOAEP(&conf.DomainKey.PublicKey, ossConfig) + + _, err := conf.KusciaClient.KusciaV1alpha1().DomainDataSources(mockDomain).Create(ctx, &v1alpha1.DomainDataSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: o.ossDataSourceID, + }, + Spec: v1alpha1.DomainDataSourceSpec{ + URI: o.ossEndpoint, + Type: common.DomainDataSourceTypeOSS, + Name: o.ossDataSourceID, + AccessDirectly: false, + Data: map[string]string{"encryptedInfo": strConfig}, + }, + }, v1.CreateOptions{}) + + return err + } + return nil +} diff --git a/cmd/kuscia/autonomy/autonomy.go b/cmd/kuscia/autonomy/autonomy.go index 75789daf..e4962e27 100644 --- a/cmd/kuscia/autonomy/autonomy.go +++ b/cmd/kuscia/autonomy/autonomy.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package autonomy import ( @@ -59,12 +59,12 @@ func Run(ctx context.Context, configFile string, onlyControllers bool) error { nlog.Info("Scheduler and controllers are all started") // wait any controller failed } else { - coreDNSModule := modules.RunCoreDNSWithDestroy(conf) + coreDnsModule := modules.RunCoreDNSWithDestroy(conf) modules.RunK3sWithDestroy(conf) // make clients after k3s start conf.MakeClients() - cdsModule, ok := coreDNSModule.(*modules.CorednsModule) + cdsModule, ok := coreDnsModule.(*modules.CorednsModule) if !ok { return errors.New("coredns module type is invalid") } @@ -111,5 +111,6 @@ func Run(ctx context.Context, configFile string, onlyControllers bool) error { modules.SetKusciaOOMScore() } conf.WaitAllModulesDone(ctx.Done()) + nlog.Errorf("Autonomy [%s] shut down......", kusciaConf.DomainID) return nil } diff --git a/cmd/kuscia/confloader/config.go b/cmd/kuscia/confloader/config.go index 768c8a3c..65d4f495 100644 --- a/cmd/kuscia/confloader/config.go +++ b/cmd/kuscia/confloader/config.go @@ -54,6 +54,8 @@ type KusciaConfig struct { DebugPort int `yaml:"debugPort"` CtrDebugPort int `yaml:"controllerDebugPort"` + Image ImageConfig `yaml:"image"` + Agent config.AgentConfig `yaml:"agent,omitempty"` Master kusciaconfig.MasterConfig `yaml:"master,omitempty"` ConfManager cmconf.ConfManagerConfig `yaml:"confManager,omitempty"` diff --git a/cmd/kuscia/confloader/kuscia_config.go b/cmd/kuscia/confloader/kuscia_config.go index b4e06818..da899785 100644 --- a/cmd/kuscia/confloader/kuscia_config.go +++ b/cmd/kuscia/confloader/kuscia_config.go @@ -179,6 +179,7 @@ func (lite *LiteKusciaConfig) OverwriteKusciaConfig(kusciaConfig *KusciaConfig) kusciaConfig.DomainRoute.DomainCsrData = GenerateCsrData(lite.DomainID, lite.DomainKeyData, lite.LiteDeployToken) kusciaConfig.Debug = lite.Debug kusciaConfig.DebugPort = lite.DebugPort + kusciaConfig.Image = lite.Image overwriteKusciaConfigLogrotate(&kusciaConfig.Logrotate, &lite.AdvancedConfig.Logrotate) } @@ -246,6 +247,7 @@ func (autonomy *AutomonyKusciaConfig) OverwriteKusciaConfig(kusciaConfig *Kuscia kusciaConfig.Debug = autonomy.Debug kusciaConfig.DebugPort = autonomy.DebugPort kusciaConfig.EnableWorkloadApprove = autonomy.AdvancedConfig.EnableWorkloadApprove + kusciaConfig.Image = autonomy.Image overwriteKusciaConfigLogrotate(&kusciaConfig.Logrotate, &autonomy.AdvancedConfig.Logrotate) } diff --git a/cmd/kuscia/kusciainit/init.go b/cmd/kuscia/kusciainit/init.go index 2ab6e09a..f2dd9493 100644 --- a/cmd/kuscia/kusciainit/init.go +++ b/cmd/kuscia/kusciainit/init.go @@ -11,6 +11,7 @@ import ( "github.com/secretflow/kuscia/cmd/kuscia/confloader" "github.com/secretflow/kuscia/pkg/common" "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/pkg/utils/resources" "github.com/secretflow/kuscia/pkg/utils/tls" ) @@ -22,10 +23,13 @@ func NewInitCommand(ctx context.Context) *cobra.Command { Long: `Init means init Kuscia config`, SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { + if config.DomainID == common.UnSupportedDomainID { + nlog.Fatal("Input domain id can't be 'master', please choose another name") + } kusciaConfig := config.convert2KusciaConfig() out, err := yaml.Marshal(kusciaConfig) if err != nil { - nlog.Fatalf("Invalid kusciaConfig, err: %v", err) + nlog.Fatalf("Failed to convert to YAML format, err: %v", err) } fmt.Println(string(out)) @@ -60,13 +64,17 @@ type InitConfig struct { func (config *InitConfig) convert2KusciaConfig() interface{} { var kusciaConfig interface{} + if err := validateConfig(config); err != nil { + nlog.Fatalf("Invalid config, err: %v", err) + } + mode := strings.ToLower(config.Mode) switch mode { - case "lite": + case common.RunModeLite: kusciaConfig = config.convert2LiteKusciaConfig() - case "master": + case common.RunModeMaster: kusciaConfig = config.convert2MasterKusciaConfig() - case "autonomy": + case common.RunModeAutonomy: kusciaConfig = config.convert2AutonomyKusciaConfig() default: nlog.Fatalf("Unsupported mode: %s", mode) @@ -74,6 +82,31 @@ func (config *InitConfig) convert2KusciaConfig() interface{} { return kusciaConfig } +func validateConfig(config *InitConfig) error { + if len(config.DomainID) == 0 { + return fmt.Errorf("invalid domain: empty not allowed") + } + + err := resources.ValidateK8sName(config.DomainID, "DomainID") + if err != nil { + return fmt.Errorf("invalid domain: must conform to a regular expression `%s`", common.K3sRegex) + } + + // validate runtime + mode := strings.ToLower(config.Mode) + if mode != common.RunModeMaster { + if !isSupportedRuntime(config.Runtime) { + return fmt.Errorf("unsupported runtime: %s", config.Runtime) + } + } + + if !isSupportedProtocol(config.Protocol) { + return fmt.Errorf("unsupported protocol: %s", config.Protocol) + } + + return nil +} + func (config *InitConfig) convert2LiteKusciaConfig() confloader.LiteKusciaConfig { kusciaConfig := confloader.LiteKusciaConfig{ CommonConfig: config.convert2CommonConfig(), @@ -142,3 +175,25 @@ func loadDomainKeyData(domainKeyFile string) (string, error) { } return domainKeyData, nil } + +func isSupportedRuntime(runtime string) bool { + switch runtime { + case "runc", "runk", "runp": + return true + default: + return false + } +} + +func isSupportedProtocol(protocol string) bool { + if protocol == "" { + return true + } + protocol = strings.ToUpper(protocol) + switch common.Protocol(protocol) { + case common.NOTLS, common.TLS, common.MTLS: + return true + default: + return false + } +} diff --git a/cmd/kuscia/kusciainit/init_test.go b/cmd/kuscia/kusciainit/init_test.go index 88202639..b11ac612 100644 --- a/cmd/kuscia/kusciainit/init_test.go +++ b/cmd/kuscia/kusciainit/init_test.go @@ -4,10 +4,11 @@ import ( "path" "testing" + "github.com/stretchr/testify/assert" + "github.com/secretflow/kuscia/cmd/kuscia/confloader" "github.com/secretflow/kuscia/pkg/utils/paths" "github.com/secretflow/kuscia/pkg/utils/tls" - "github.com/stretchr/testify/assert" ) func TestKusciaInitCommand_ConfigConvert_Master(t *testing.T) { @@ -121,10 +122,10 @@ func TestKusciaInitCommand_ConfigConvert_Autonomy(t *testing.T) { func TestLoadDomainKeyData_FileExists(t *testing.T) { t.Parallel() - validkeyFile := path.Join(t.TempDir(), "domain.key") - assert.Nil(t, tls.GeneratePrivateKeyToFile(validkeyFile)) + validKeyFile := path.Join(t.TempDir(), "domain.key") + assert.Nil(t, tls.GeneratePrivateKeyToFile(validKeyFile)) - _, err := loadDomainKeyData(validkeyFile) + _, err := loadDomainKeyData(validKeyFile) assert.Nil(t, err) } diff --git a/cmd/kuscia/lite/lite.go b/cmd/kuscia/lite/lite.go index e136142b..466ebb70 100644 --- a/cmd/kuscia/lite/lite.go +++ b/cmd/kuscia/lite/lite.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package lite import ( @@ -49,7 +49,7 @@ func Run(ctx context.Context, configFile string) error { conf := modules.InitDependencies(ctx, kusciaConf) defer conf.Close() - coreDNSModule := modules.RunCoreDNSWithDestroy(conf) + coreDnsModule := modules.RunCoreDNSWithDestroy(conf) conf.MakeClients() @@ -72,7 +72,7 @@ func Run(ctx context.Context, configFile string) error { }() wg.Wait() - cdsModule, ok := coreDNSModule.(*modules.CorednsModule) + cdsModule, ok := coreDnsModule.(*modules.CorednsModule) if !ok { return errors.New("coredns module type is invalid") } @@ -90,6 +90,6 @@ func Run(ctx context.Context, configFile string) error { utils.SetupPprof(conf.Debug, conf.DebugPort, false) modules.SetKusciaOOMScore() conf.WaitAllModulesDone(ctx.Done()) - nlog.Errorf("Lite shut down......") + nlog.Errorf("Lite [%s] shut down......", kusciaConf.DomainID) return nil } diff --git a/cmd/kuscia/main.go b/cmd/kuscia/main.go index 1efb30b1..16928263 100644 --- a/cmd/kuscia/main.go +++ b/cmd/kuscia/main.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package main import ( diff --git a/cmd/kuscia/master/master.go b/cmd/kuscia/master/master.go index db84d87b..b8867a62 100644 --- a/cmd/kuscia/master/master.go +++ b/cmd/kuscia/master/master.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package master import ( @@ -67,7 +67,7 @@ func Run(ctx context.Context, configFile string, onlyControllers bool) error { nlog.Info("Scheduler and controllers are all started") // wait any controller failed } else { - coreDNSModule := modules.RunCoreDNSWithDestroy(conf) + coreDnsModule := modules.RunCoreDNSWithDestroy(conf) if err := modules.RunK3sWithDestroy(conf); err != nil { nlog.Errorf("k3s start failed: %s", err) return err @@ -75,7 +75,7 @@ func Run(ctx context.Context, configFile string, onlyControllers bool) error { // make clients after k3s start conf.MakeClients() - cdsModule, ok := coreDNSModule.(*modules.CorednsModule) + cdsModule, ok := coreDnsModule.(*modules.CorednsModule) if !ok { return errors.New("coredns module type is invalid") } @@ -115,5 +115,6 @@ func Run(ctx context.Context, configFile string, onlyControllers bool) error { modules.SetKusciaOOMScore() } conf.WaitAllModulesDone(ctx.Done()) + nlog.Errorf("Master [%s] shut down......", kusciaConf.DomainID) return nil } diff --git a/cmd/kuscia/modules/agent.go b/cmd/kuscia/modules/agent.go index b835f709..001deb24 100644 --- a/cmd/kuscia/modules/agent.go +++ b/cmd/kuscia/modules/agent.go @@ -26,6 +26,7 @@ import ( "github.com/secretflow/kuscia/pkg/agent/commands" "github.com/secretflow/kuscia/pkg/agent/config" "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/embedstrings" "github.com/secretflow/kuscia/pkg/kusciaapi/utils" "github.com/secretflow/kuscia/pkg/utils/kubeconfig" "github.com/secretflow/kuscia/pkg/utils/meta" @@ -67,9 +68,28 @@ func NewAgent(i *Dependencies) Module { conf.AllowPrivileged = i.Agent.AllowPrivileged conf.Provider.CRI.RemoteImageEndpoint = fmt.Sprintf("unix://%s", i.ContainerdSock) conf.Provider.CRI.RemoteRuntimeEndpoint = fmt.Sprintf("unix://%s", i.ContainerdSock) - conf.Registry.Default.Repository = os.Getenv("REGISTRY_ENDPOINT") - conf.Registry.Default.Username = os.Getenv("REGISTRY_USERNAME") - conf.Registry.Default.Password = os.Getenv("REGISTRY_PASSWORD") + + if i.Image != nil && len(i.Image.Registries) > 0 { + defaultRegIdx := 0 // use first registry as default registry + for idx, reg := range i.Image.Registries { + nlog.Infof("registry(%s), endpoint(%s)", reg.Name, reg.Endpoint) + conf.Registry.Allows = append(conf.Registry.Allows, config.RegistryAuth{ + Repository: reg.Endpoint, + Username: reg.UserName, + Password: reg.Password, + }) + + if i.Image.DefaultRegistry == reg.Name { + defaultRegIdx = idx + } + } + + conf.Registry.Default = conf.Registry.Allows[defaultRegIdx] + } else { // deprecated. remove it 1year later + conf.Registry.Default.Repository = os.Getenv("REGISTRY_ENDPOINT") + conf.Registry.Default.Username = os.Getenv("REGISTRY_USERNAME") + conf.Registry.Default.Password = os.Getenv("REGISTRY_PASSWORD") + } conf.Plugins = i.Agent.Plugins conf.KusciaAPIProtocol = i.KusciaAPI.Protocol @@ -102,7 +122,7 @@ func (agent *agentModule) Run(ctx context.Context) error { } func (agent *agentModule) execPreCmds(ctx context.Context) error { - cmd := exec.CommandContext(ctx, "sh", "-c", filepath.Join(agent.conf.RootDir, "scripts/deploy/cgroup_pre_detect.sh")) + cmd := exec.CommandContext(ctx, "sh", "-c", embedstrings.CGrouptPreDetectScript) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout return cmd.Run() @@ -126,7 +146,7 @@ func (agent *agentModule) Name() string { func RunAgentWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(2 * time.Second) + shutdownEntry := NewShutdownHookEntry(2 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "agent", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/allinone_operator.go b/cmd/kuscia/modules/allinone_operator.go index 19a2e2b0..b2d89774 100644 --- a/cmd/kuscia/modules/allinone_operator.go +++ b/cmd/kuscia/modules/allinone_operator.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package modules import ( @@ -54,7 +54,7 @@ func RunOperatorsInSubProcess(ctx context.Context, cancel context.CancelFunc) er } args := append(os.Args[1:], "--controllers") nlog.Infof("Subprocess args: %v", args) - cmd := exec.CommandContext(ctx, path, args...) + cmd := exec.Command(path, args...) cmd.Env = os.Environ() cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout diff --git a/cmd/kuscia/modules/confmanager.go b/cmd/kuscia/modules/confmanager.go index caa31f2f..a4555bc1 100644 --- a/cmd/kuscia/modules/confmanager.go +++ b/cmd/kuscia/modules/confmanager.go @@ -187,7 +187,7 @@ func (m confManagerModule) readyZ() bool { func RunConfManagerWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "confmanager", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/containerd.go b/cmd/kuscia/modules/containerd.go index 62c38ed3..d5dd0e96 100644 --- a/cmd/kuscia/modules/containerd.go +++ b/cmd/kuscia/modules/containerd.go @@ -23,6 +23,7 @@ import ( "time" pkgcom "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/embedstrings" "github.com/secretflow/kuscia/pkg/utils/common" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/nlog/ljwriter" @@ -76,7 +77,7 @@ func (s *containerdModule) Run(ctx context.Context) error { lj, _ := ljwriter.New(&s.LogConfig) n := nlog.NewNLog(nlog.SetWriter(lj)) return sp.Run(ctx, func(ctx context.Context) supervisor.Cmd { - cmd := exec.CommandContext(ctx, filepath.Join(s.Root, "bin/containerd"), args...) + cmd := exec.Command(filepath.Join(s.Root, "bin/containerd"), args...) cmd.Stderr = n cmd.Stdout = n return &ModuleCMD{ @@ -87,7 +88,7 @@ func (s *containerdModule) Run(ctx context.Context) error { } func (s *containerdModule) execPreCmds(ctx context.Context) error { - cmd := exec.CommandContext(ctx, "sh", "-c", filepath.Join(s.Root, "scripts/deploy/iptables_pre_detect.sh")) + cmd := exec.CommandContext(ctx, "sh", "-c", embedstrings.IPTablesPreDetectScript) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout err := cmd.Run() @@ -95,7 +96,7 @@ func (s *containerdModule) execPreCmds(ctx context.Context) error { return err } - cmd = exec.CommandContext(ctx, "sh", "-c", filepath.Join(s.Root, "scripts/deploy/cgroup_pre_detect.sh")) + cmd = exec.CommandContext(ctx, "sh", "-c", embedstrings.CGrouptPreDetectScript) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout return cmd.Run() @@ -150,7 +151,7 @@ func (s *containerdModule) Name() string { func RunContainerdWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "containerd", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/controllers.go b/cmd/kuscia/modules/controllers.go index 380c56cf..c1246ac6 100644 --- a/cmd/kuscia/modules/controllers.go +++ b/cmd/kuscia/modules/controllers.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package modules import ( @@ -87,7 +87,7 @@ func NewControllersModule(i *Dependencies) Module { func RunControllerWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "controllers", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/coredns.go b/cmd/kuscia/modules/coredns.go index e9da7fa6..4d815288 100644 --- a/cmd/kuscia/modules/coredns.go +++ b/cmd/kuscia/modules/coredns.go @@ -156,7 +156,7 @@ func (s *CorednsModule) Name() string { func RunCoreDNSWithDestroy(conf *Dependencies) Module { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "coredns", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/datamesh.go b/cmd/kuscia/modules/datamesh.go index 934d1549..ba18b016 100644 --- a/cmd/kuscia/modules/datamesh.go +++ b/cmd/kuscia/modules/datamesh.go @@ -143,7 +143,7 @@ func (m dataMeshModule) readyZ() bool { func RunDataMeshWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "datamesh", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/domainroute.go b/cmd/kuscia/modules/domainroute.go index 404b48c3..fd5727fb 100644 --- a/cmd/kuscia/modules/domainroute.go +++ b/cmd/kuscia/modules/domainroute.go @@ -130,7 +130,7 @@ func (d *domainRouteModule) Name() string { func RunDomainRouteWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(2 * time.Second) + shutdownEntry := NewShutdownHookEntry(2 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "domainroute", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/envoy.go b/cmd/kuscia/modules/envoy.go index 5998a25d..5bc2e53c 100644 --- a/cmd/kuscia/modules/envoy.go +++ b/cmd/kuscia/modules/envoy.go @@ -119,7 +119,7 @@ func (s *envoyModule) Run(ctx context.Context) error { go s.logRotate(ctx, configFilePath) return sp.Run(ctx, func(ctx context.Context) supervisor.Cmd { - cmd := exec.CommandContext(ctx, filepath.Join(s.rootDir, "bin/envoy"), args...) + cmd := exec.Command(filepath.Join(s.rootDir, "bin/envoy"), args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = os.Environ() @@ -197,7 +197,7 @@ func (s *envoyModule) readCommandArgs() (*EnvoyCommandLineConfig, error) { func RunEnvoyWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "envoy", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/interconn.go b/cmd/kuscia/modules/interconn.go index 2d8a1b57..25a28f33 100644 --- a/cmd/kuscia/modules/interconn.go +++ b/cmd/kuscia/modules/interconn.go @@ -28,7 +28,7 @@ func NewInterConn(ctx context.Context, deps *Dependencies) (Module, error) { func RunInterConnWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "interconn", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/k3s.go b/cmd/kuscia/modules/k3s.go index d952d0cb..35490236 100644 --- a/cmd/kuscia/modules/k3s.go +++ b/cmd/kuscia/modules/k3s.go @@ -210,7 +210,7 @@ func (s *k3sModule) Run(ctx context.Context) error { n := nlog.NewNLog(nlog.SetWriter(lj)) return sp.Run(ctx, func(ctx context.Context) supervisor.Cmd { - cmd := exec.CommandContext(ctx, filepath.Join(s.rootDir, "bin/k3s"), args...) + cmd := exec.Command(filepath.Join(s.rootDir, "bin/k3s"), args...) cmd.Stderr = n cmd.Stdout = n @@ -249,7 +249,7 @@ func (s *k3sModule) Name() string { func RunK3sWithDestroy(conf *Dependencies) error { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "k3s", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/kusciaapi.go b/cmd/kuscia/modules/kusciaapi.go index 8093b4dc..02284a6a 100644 --- a/cmd/kuscia/modules/kusciaapi.go +++ b/cmd/kuscia/modules/kusciaapi.go @@ -29,12 +29,14 @@ import ( "google.golang.org/grpc/credentials" "k8s.io/client-go/kubernetes" + "github.com/secretflow/kuscia/cmd/kuscia/confloader" "github.com/secretflow/kuscia/pkg/common" kusciaclientset "github.com/secretflow/kuscia/pkg/crd/clientset/versioned" "github.com/secretflow/kuscia/pkg/kusciaapi/commands" "github.com/secretflow/kuscia/pkg/kusciaapi/config" apiutils "github.com/secretflow/kuscia/pkg/kusciaapi/utils" "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/pkg/utils/nlog/zlogwriter" "github.com/secretflow/kuscia/pkg/utils/paths" tlsutils "github.com/secretflow/kuscia/pkg/utils/tls" "github.com/secretflow/kuscia/pkg/web/constants" @@ -44,7 +46,8 @@ import ( ) const ( - kusciaAPISanDNSName = "kusciaapi" + kusciaAPISanDNSName = "kusciaapi" + kusciaAPIInterceptorLoggerPath = "var/logs/kusciaapi.log" ) type kusciaAPIModule struct { @@ -99,7 +102,13 @@ func NewKusciaAPI(d *Dependencies) (Module, error) { } } } + interceptorLogger, err := initInterceptorLogger(d.KusciaConfig) + if err != nil { + nlog.Errorf("Init Kuscia API interceptor logger failed: %v", err) + return nil, err + } + kusciaAPIConfig.InterceptorLog = interceptorLogger nlog.Debugf("Kuscia api config is %+v", kusciaAPIConfig) return &kusciaAPIModule{ @@ -109,6 +118,15 @@ func NewKusciaAPI(d *Dependencies) (Module, error) { }, nil } +func initInterceptorLogger(kusciaConf confloader.KusciaConfig) (*nlog.NLog, error) { + logConfig := initLoggerConfig(kusciaConf, kusciaAPIInterceptorLoggerPath) + logWriter, err := zlogwriter.New(logConfig) + if err != nil { + return nil, err + } + return nlog.NewNLog(nlog.SetWriter(logWriter), nlog.SetFormatter(nlog.NewGinLogFormatter())), nil +} + func (m kusciaAPIModule) Run(ctx context.Context) error { return commands.Run(ctx, m.conf, m.kusciaClient, m.kubeClient) } @@ -241,7 +259,7 @@ func (m kusciaAPIModule) readyZ() bool { func RunKusciaAPIWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "kusciaapi", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/metricexporter.go b/cmd/kuscia/modules/metricexporter.go index e6f8bb4f..a6bd42fc 100644 --- a/cmd/kuscia/modules/metricexporter.go +++ b/cmd/kuscia/modules/metricexporter.go @@ -103,7 +103,7 @@ func (exporter *metricExporterModule) Name() string { func RunMetricExporterWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "metricexporter", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/modules.go b/cmd/kuscia/modules/modules.go index d743a993..d0090829 100644 --- a/cmd/kuscia/modules/modules.go +++ b/cmd/kuscia/modules/modules.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package modules import ( @@ -23,6 +23,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "sync" "sync/atomic" "time" @@ -47,6 +48,7 @@ import ( var ( k3sDataDirPrefix = "var/k3s/" + kusciaLogPath = "var/logs/kuscia.log" defaultInterConnSchedulerPort = 8084 defaultEndpointForLite = "http://apiserver.master.svc" ) @@ -70,6 +72,7 @@ type Dependencies struct { KusciaKubeConfig string EnableContainerd bool SecretBackendHolder *secretbackend.Holder + Image *confloader.ImageConfig DomainCertByMasterValue atomic.Value // the value is <*x509.Certificate> LogConfig *nlog.LogConfig Logrorate confloader.LogrotateConfig @@ -147,7 +150,7 @@ type shutdownHookEntry struct { timeOut time.Duration } -func newShutdownHookEntry(timeout time.Duration) *shutdownHookEntry { +func NewShutdownHookEntry(timeout time.Duration) *shutdownHookEntry { return &shutdownHookEntry{ timeOut: timeout, closeCh: make(chan struct{}), @@ -304,25 +307,31 @@ func InitLogs(logConfig *nlog.LogConfig) error { return nil } +func initLoggerConfig(kusciaConf confloader.KusciaConfig, logPath string) *nlog.LogConfig { + return &nlog.LogConfig{ + LogPath: logPath, + LogLevel: kusciaConf.LogLevel, + MaxFileSizeMB: kusciaConf.Logrotate.MaxFileSizeMB, + MaxFiles: kusciaConf.Logrotate.MaxFiles, + Compress: true, + } +} + func InitDependencies(ctx context.Context, kusciaConf confloader.KusciaConfig) *Dependencies { dependencies := &Dependencies{ KusciaConfig: kusciaConf, } dependencies.stopCh = make(chan struct{}) // init log - logConfig := &nlog.LogConfig{ - LogLevel: kusciaConf.LogLevel, - LogPath: "var/logs/kuscia.log", - MaxFileSizeMB: kusciaConf.Logrotate.MaxFileSizeMB, - MaxFiles: kusciaConf.Logrotate.MaxFiles, - Compress: true, - } + logConfig := initLoggerConfig(kusciaConf, kusciaLogPath) if err := InitLogs(logConfig); err != nil { nlog.Fatal(err) } + nlog.Debugf("Read kuscia config: %+v", kusciaConf) dependencies.LogConfig = logConfig dependencies.Logrorate = kusciaConf.Logrotate + dependencies.Image = &kusciaConf.Image // run config loader dependencies.SecretBackendHolder = secretbackend.NewHolder() nlog.Info("Start to init all secret backends ... ") @@ -404,6 +413,17 @@ func (c *ModuleCMD) Wait() error { return c.cmd.Wait() } +func (c *ModuleCMD) Stop() error { + if c.cmd != nil && c.cmd.Process != nil { + // Windows doesn't support Interrupt + if runtime.GOOS == "windows" { + return c.cmd.Process.Signal(os.Kill) + } + return c.cmd.Process.Signal(os.Interrupt) + } + return nil +} + func (c *ModuleCMD) Pid() int { if c.cmd != nil && c.cmd.Process != nil { return c.cmd.Process.Pid diff --git a/cmd/kuscia/modules/nodeexporter.go b/cmd/kuscia/modules/nodeexporter.go index 63c08d2b..87519fe1 100644 --- a/cmd/kuscia/modules/nodeexporter.go +++ b/cmd/kuscia/modules/nodeexporter.go @@ -50,7 +50,7 @@ func (exporter *nodeExporterModule) Run(ctx context.Context) error { args = append(args, disabledCollectors...) sp := supervisor.NewSupervisor("node_exporter", nil, -1) return sp.Run(ctx, func(ctx context.Context) supervisor.Cmd { - cmd := exec.CommandContext(ctx, filepath.Join(exporter.rootDir, "bin/node_exporter"), args...) + cmd := exec.Command(filepath.Join(exporter.rootDir, "bin/node_exporter"), args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = os.Environ() @@ -112,7 +112,7 @@ func (exporter *nodeExporterModule) Name() string { func RunNodeExporterWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "nodeexporter", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/scheduler.go b/cmd/kuscia/modules/scheduler.go index e21c39d7..a090a27b 100644 --- a/cmd/kuscia/modules/scheduler.go +++ b/cmd/kuscia/modules/scheduler.go @@ -112,7 +112,7 @@ func (s *schedulerModule) Name() string { func RunSchedulerWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "kusciascheduler", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/ssexporter.go b/cmd/kuscia/modules/ssexporter.go index e45a369d..5e8abbfb 100644 --- a/cmd/kuscia/modules/ssexporter.go +++ b/cmd/kuscia/modules/ssexporter.go @@ -99,7 +99,7 @@ func (exporter *ssExporterModule) Name() string { func RunSsExporterWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(500 * time.Millisecond) + shutdownEntry := NewShutdownHookEntry(500 * time.Millisecond) conf.RegisterDestroyFunc(DestroyFunc{ Name: "ssexporter", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/modules/transport.go b/cmd/kuscia/modules/transport.go index 2ac6f35e..e4b367d6 100644 --- a/cmd/kuscia/modules/transport.go +++ b/cmd/kuscia/modules/transport.go @@ -71,7 +71,7 @@ func (t *transportModule) runAsSubProcess(ctx context.Context) error { sp := supervisor.NewSupervisor(transportModuleName, nil, -1) return sp.Run(ctx, func(ctx context.Context) supervisor.Cmd { - cmd := exec.CommandContext(ctx, filepath.Join(t.rootDir, transportBinPath), args...) + cmd := exec.Command(filepath.Join(t.rootDir, transportBinPath), args...) cmd.Env = os.Environ() return &ModuleCMD{ cmd: cmd, @@ -116,7 +116,7 @@ func (t *transportModule) readyz(address string) error { func RunTransportWithDestroy(conf *Dependencies) { runCtx, cancel := context.WithCancel(context.Background()) - shutdownEntry := newShutdownHookEntry(1 * time.Second) + shutdownEntry := NewShutdownHookEntry(1 * time.Second) conf.RegisterDestroyFunc(DestroyFunc{ Name: "transport", DestroyCh: runCtx.Done(), diff --git a/cmd/kuscia/start/start.go b/cmd/kuscia/start/start.go index d2ec8d09..c32d1e9e 100644 --- a/cmd/kuscia/start/start.go +++ b/cmd/kuscia/start/start.go @@ -24,6 +24,7 @@ import ( "github.com/secretflow/kuscia/cmd/kuscia/confloader" "github.com/secretflow/kuscia/cmd/kuscia/lite" "github.com/secretflow/kuscia/cmd/kuscia/master" + "github.com/secretflow/kuscia/pkg/common" "github.com/secretflow/kuscia/pkg/utils/nlog" ) @@ -38,12 +39,17 @@ func NewStartCommand(ctx context.Context) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { commonConfig := confloader.LoadCommonConfig(configFile) mode := strings.ToLower(commonConfig.Mode) + + if commonConfig.DomainID == common.UnSupportedDomainID { + nlog.Fatalf("Domain id can't be 'master', please check input config file(%s)", configFile) + } + switch mode { - case "lite": + case common.RunModeLite: lite.Run(ctx, configFile) - case "master": + case common.RunModeMaster: master.Run(ctx, configFile, onlyControllers) - case "autonomy": + case common.RunModeAutonomy: autonomy.Run(ctx, configFile, onlyControllers) default: nlog.Fatalf("Unsupported mode: %s", commonConfig.Mode) diff --git a/cmd/kuscia/utils/debug.go b/cmd/kuscia/utils/debug.go index c667656c..70812d49 100644 --- a/cmd/kuscia/utils/debug.go +++ b/cmd/kuscia/utils/debug.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package utils import ( diff --git a/crds/v1alpha1/kuscia.secretflow_clusterdomainroutes.yaml b/crds/v1alpha1/kuscia.secretflow_clusterdomainroutes.yaml index adf471c3..f0226565 100644 --- a/crds/v1alpha1/kuscia.secretflow_clusterdomainroutes.yaml +++ b/crds/v1alpha1/kuscia.secretflow_clusterdomainroutes.yaml @@ -271,7 +271,12 @@ spec: type: array expirationTime: description: Timestamp representing the time when this revision - expirated. + expired. + format: date-time + type: string + heartBeatTime: + description: Timestamp representing the time when this revision + heartbeat. format: date-time type: string isReady: @@ -315,7 +320,12 @@ spec: type: array expirationTime: description: Timestamp representing the time when this revision - expirated. + expired. + format: date-time + type: string + heartBeatTime: + description: Timestamp representing the time when this revision + heartbeat. format: date-time type: string isReady: diff --git a/crds/v1alpha1/kuscia.secretflow_domainroutes.yaml b/crds/v1alpha1/kuscia.secretflow_domainroutes.yaml index 89a225e2..0ff035af 100644 --- a/crds/v1alpha1/kuscia.secretflow_domainroutes.yaml +++ b/crds/v1alpha1/kuscia.secretflow_domainroutes.yaml @@ -225,7 +225,12 @@ spec: type: array expirationTime: description: Timestamp representing the time when this revision - expirated. + expired. + format: date-time + type: string + heartBeatTime: + description: Timestamp representing the time when this revision + heartbeat. format: date-time type: string isReady: @@ -258,7 +263,12 @@ spec: type: array expirationTime: description: Timestamp representing the time when this revision - expirated. + expired. + format: date-time + type: string + heartBeatTime: + description: Timestamp representing the time when this revision + heartbeat. format: date-time type: string isReady: diff --git a/crds/v1alpha1/kuscia.secretflow_taskresourcegroups.yaml b/crds/v1alpha1/kuscia.secretflow_taskresourcegroups.yaml index d676d72e..4fb5de1d 100644 --- a/crds/v1alpha1/kuscia.secretflow_taskresourcegroups.yaml +++ b/crds/v1alpha1/kuscia.secretflow_taskresourcegroups.yaml @@ -138,7 +138,6 @@ spec: required: - initiator - minReservedMembers - - parties type: object status: description: TaskResourceGroupStatus defines the details of task group diff --git a/docs/deployment/Docker_deployment_kuscia/deploy_master_lite_cn.md b/docs/deployment/Docker_deployment_kuscia/deploy_master_lite_cn.md index fffc279e..d0f4b4d7 100644 --- a/docs/deployment/Docker_deployment_kuscia/deploy_master_lite_cn.md +++ b/docs/deployment/Docker_deployment_kuscia/deploy_master_lite_cn.md @@ -11,17 +11,21 @@ ## 部署流程(基于TOKEN认证) ### 部署 master 节点 -登录到安装 master 的机器上,假设对外ip是1.1.1.1。 + +登录到安装 master 的机器上,假设对外 IP 是 1.1.1.1。 + 指定 Kuscia 版本: + ```bash -# 使用的 Kuscia 镜像,这里使用 0.9.0b0 版本镜像 -export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.9.0b0 +# 使用的 Kuscia 镜像,这里使用 0.10.0b0 版本镜像 +export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.10.0b0 ``` 指定 Secretflow 版本: + ```bash -# 使用的 Secretflow 镜像,这里使用 1.7.0b0 版本镜像 -export SECRETFLOW_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:1.7.0b0 +# 使用的 Secretflow 镜像,这里使用 1.8.0b0 版本镜像 +export SECRETFLOW_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:1.8.0b0 ``` 获取部署脚本,部署脚本会下载到当前目录: @@ -30,12 +34,15 @@ export SECRETFLOW_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretfl docker pull $KUSCIA_IMAGE && docker run --rm $KUSCIA_IMAGE cat /home/kuscia/scripts/deploy/kuscia.sh > kuscia.sh && chmod u+x kuscia.sh ``` -生成 master 节点的配置文件: +生成 master 节点的配置文件,kuscia init 参数请参考[kuscia 配置文件](../kuscia_config_cn.md#id3): + ```bash -# -n 参数传递的是 master 节点 ID,DomainID 需全局唯一,生产环境建议使用公司名称-部门名称-节点名称,如: mycompany-secretflow-master -docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode master --domain "mycompany-secretflow-master" > kuscia_master.yaml +# --domain 参数传递的是 master 节点 ID,DomainID 需全局唯一并且符合 RFC 1123 标签名规范,详情参考: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names,生产环境建议使用公司名称-部门名称-节点名称,如: mycompany-secretflow-master +docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode master --domain "mycompany-secretflow-master" > kuscia_master.yaml 2>&1 || cat kuscia_master.yaml ``` +建议检查生成的文件,避免配置文件错误导致的部署启动问题。 + 启动 master,默认会在当前目录下创建 ${USER}-kuscia-master/{data、logs} 用来存储 master 的数据、日志: ```bash @@ -45,14 +52,18 @@ docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode master --domain "mycompan # -m 或者 --memory-limit 参数给节点容器设置适当的内存限制。例如,'-m 4GiB 或 --memory-limit=4GiB' 表示限制最大内存 4GiB,'-m -1 或 --memory-limit=-1'表示没有限制,不设置默认 master 为 2GiB,lite 节点 4GiB,autonomy 节点 6GiB。 ./kuscia.sh start -c kuscia_master.yaml -p 18080 -k 18081 ``` -> 注意事项:
-> - 目前 kuscia.sh 脚本仅支持导入 Secretflow 镜像,scql、serving 以及其他自定义镜像请移步至[注册自定义算法镜像](../../development/register_custom_image.md)
-> - 如果 master 的入口网络存在网关时,为了确保节点与 master 之间通信正常,需要网关符合一些要求,详情请参考[这里](../networkrequirements.md)
-> - master 节点默认使用 sqlite 作为存储,如果生产部署,需要配置链接到 mysql 数据库的连接串,具体配置可以参考[这里](../kuscia_config_cn.md#id3)
-> - 需要对合作方暴露的 Kuscia 端口,可参考 [Kuscia 端口介绍](../kuscia_ports_cn.md) + +:::{tip} +- 节点 ID 需要符合 RFC 1123 标签名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) +- 目前 kuscia.sh 脚本仅支持导入 Secretflow 镜像,scql、serving 以及其他自定义镜像请移步至[注册自定义算法镜像](../../development/register_custom_image.md) +- 如果 master 的入口网络存在网关时,为了确保节点与 master 之间通信正常,需要网关符合一些要求,详情请参考[这里](../networkrequirements.md) +- master 节点默认使用 sqlite 作为存储,如果生产部署,需要配置链接到 mysql 数据库的连接串,具体配置可以参考[这里](../kuscia_config_cn.md#id3) +- 需要对合作方暴露的 Kuscia 端口,可参考 [Kuscia 端口介绍](../kuscia_ports_cn.md) +::: 建议使用 curl -kvvv https://ip:port; 检查一下是否访问能通,正常情况下返回的 HTTP 错误码是 401,内容是:unauthorized。 示例如下: + ```bash * Trying 127.0.0.1:18080... * Connected to 127.0.0.1 (127.0.0.1) port 18080 (#0) @@ -94,44 +105,56 @@ docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode master --domain "mycompan * Connection #0 to host 127.0.0.1 left intact unauthorized ``` + #### Tips -本文后续还会经常使用到 docker exec -it ${USER}-kuscia-master xxxxx 类似的命令。建议以如下方式简化输入。 + +本文后续还会经常使用到 `docker exec -it ${USER}-kuscia-master xxxxx` 类似的命令。建议以如下方式简化输入。 + ```bash alias km="docker exec -it ${USER}-kuscia-master" ``` -后续相关命令可以简化为km xxxxx。 + +后续相关命令可以简化为 `km xxxxx`。 ### 部署 lite 节点 -你可以选择在任一台机器上部署 lite 节点 。 +你可以选择在任一台机器上部署 lite 节点(下文以 alice、bob 为例)。 #### 部署 lite 节点 alice + 在部署 alice 节点之前,我们需要在 master 上注册 alice 节点并获取部署时需要用到的 Token 。 + 执行以下命令,完成节点注册并从返回中得到 Token (下文将以 abcdefg 为例)。 + ```bash -docker exec -it ${USER}-kuscia-master sh scripts/deploy/add_domain_lite.sh alice +docker exec -i ${USER}-kuscia-master sh scripts/deploy/add_domain_lite.sh alice ``` + 输出示例: + ```bash abcdefg ``` 如果 Token 遗忘了,可以通过该命令重新获取 + ```bash docker exec -it ${USER}-kuscia-master kubectl get domain alice -o=jsonpath='{.status.deployTokenStatuses[?(@.state=="unused")].token}' && echo ``` + 输出示例: + ```bash abcdefg ``` -注意:节点 ID 需要符合 DNS 子域名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) - 接下来,登录到安装 alice 的机器上,假设对外暴露的 IP 是 2.2.2.2。 + 指定 Kuscia 版本: + ```bash -# 使用的 Kuscia 镜像,这里使用 0.9.0b0 版本镜像 -export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.9.0b0 +# 使用的 Kuscia 镜像,这里使用 0.10.0b0 版本镜像 +export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.10.0b0 ``` 获取部署脚本,部署脚本会下载到当前目录: @@ -141,49 +164,60 @@ docker pull $KUSCIA_IMAGE && docker run --rm $KUSCIA_IMAGE cat /home/kuscia/scri ``` 生成 alice 节点的配置文件: + ```bash # --domain 参数传递的是节点 ID # --lite-deploy-token 参数传递的是节点部署的 Token # --master-endpoint 参数传递的是 master 容器对外暴露的 https://IP:PORT,假设 master 对外暴露的 IP 是 1.1.1.1,端口是18080 -docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode lite --domain "alice" --master-endpoint "https://1.1.1.1:18080" --lite-deploy-token "abcdefg" > lite_alice.yaml +docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode lite --domain "alice" --master-endpoint "https://1.1.1.1:18080" --lite-deploy-token "abcdefg" > lite_alice.yaml 2>&1 || cat lite_alice.yaml ``` 启动 alice,默认会在当前目录下创建 ${USER}-kuscia-lite-alice/data 目录用来存放 alice 的数据: + ```bash # -p 参数传递的是节点容器映射到主机的端口,保证和主机上现有的端口不冲突即可 # -k 参数传递的是 lite 容器 KusciaAPI 映射到主机的 HTTP 端口,保证和主机上现有的端口不冲突即可 ./kuscia.sh start -c lite_alice.yaml -p 28080 -k 28081 ``` + > 如果 master 与多个 lite 节点部署在同一个物理机上,可以用 -p -k -g -q 参数指定下端口号(例如:./kuscia.sh start -c lite_alice.yaml -p 28080 -k 28081 -g 28082 -q 28083),防止出现端口冲突。 #### 部署 lite 节点 bob 在部署 bob 节点之前,我们需要在 master 注册 bob 节点,并获取到部署时需要用到的 Token 。 + 执行以下命令,完成节点注册并从返回中得到 Token (下文将以 hijklmn 为例)。 + ```bash -docker exec -it ${USER}-kuscia-master sh scripts/deploy/add_domain_lite.sh bob +docker exec -i ${USER}-kuscia-master sh scripts/deploy/add_domain_lite.sh bob ``` + 输出示例: + ```bash hijklmn ``` 如果 Token 遗忘了,可以通过该命令重新获取 + ```bash docker exec -it ${USER}-kuscia-master kubectl get domain bob -o=jsonpath='{.status.deployTokenStatuses[?(@.state=="unused")].token}' && echo ``` + 输出示例: + ```bash hijklmn ``` 接下来,登录到安装 bob 的机器上,假设对暴露的 IP 是 3.3.3.3。 + 指定 Kuscia 版本: + ```bash -# 使用的 Kuscia 镜像,这里使用 0.9.0b0 版本镜像 -export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.9.0b0 +# 使用的 Kuscia 镜像,这里使用 0.10.0b0 版本镜像 +export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.10.0b0 ``` -注意:节点 id 需要符合 DNS 子域名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) 获取部署脚本,部署脚本会下载到当前目录: @@ -192,14 +226,16 @@ docker pull $KUSCIA_IMAGE && docker run --rm $KUSCIA_IMAGE cat /home/kuscia/scri ``` 生成 bob 节点的配置文件: + ```bash # --domain 参数传递的是节点 ID # --lite-deploy-token 参数传递的是节点部署的 Token # --master-endpoint 参数传递的是 master 容器对外暴露的 https://IP:PORT,假设 master 对外暴露的 IP 是 1.1.1.1,端口是18080 -docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode lite --domain "bob" --master-endpoint "https://1.1.1.1:18080" --lite-deploy-token "hijklmn" > lite_bob.yaml +docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode lite --domain "bob" --master-endpoint "https://1.1.1.1:18080" --lite-deploy-token "hijklmn" > lite_bob.yaml 2>&1 || cat lite_bob.yaml ``` 启动 bob,默认会在当前目录下创建 ${USER}-kuscia-lite-bob/data 目录用来存放 bob 的数据: + ```bash # -p 参数传递的是节点容器映射到主机的端口,保证和主机上现有的端口不冲突即可 # -k 参数传递的是 lite 容器 KusciaAPI 映射到主机的 HTTP 端口,保证和主机上现有的端口不冲突即可 @@ -214,6 +250,7 @@ docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode lite --domain "bob" --mas #### 创建 alice 和 bob 之间的授权 在 master 机器上执行创建授权的命令 + ```bash # 为了减少授权错误的排查成本,建议在alice/bob容器内分别(curl)访问的对方地址判定是否能联通,之后再授权 # 示例:curl -vvv http://ip:port 返回正常的HTTP错误码是401 @@ -224,19 +261,25 @@ docker exec -it ${USER}-kuscia-master sh scripts/deploy/create_cluster_domain_ro ``` 执行以下命令: + ```bash docker exec -it ${USER}-kuscia-master kubectl get cdr ``` 当 `type` 为 Ready 的 condition 的 `status` 值为 "True" 则说明 alice 和 bob 之间授权建立成功。 -注意:如果节点之间的入口网络存在网关时,为了确保节点与节点之间通信正常,需要网关符合一些要求,详情请参考[这里](./networkrequirements.md) +:::{tip} +如果节点之间的入口网络存在网关时,为了确保节点与节点之间通信正常,需要网关符合一些要求,详情请参考[这里](./networkrequirements.md) +::: ### 运行任务 + 接下来,我们运行一个测试任务以验证部署是否成功。 + #### 准备数据 ##### 获取测试数据集 + 登录到安装 alice 的机器上,将默认的测试数据拷贝到之前部署目录的 ${USER}-kuscia-lite-alice/data 下 ```bash @@ -287,12 +330,15 @@ docker exec -it ${USER}-kuscia-master curl -X POST 'https://127.0.0.1:8082/api/v 登录到安装 master 的机器上 创建并启动作业(两方 PSI 任务) + ```bash docker exec -it ${USER}-kuscia-master scripts/user/create_example_job.sh ``` 查看作业状态 + ```bash docker exec -it ${USER}-kuscia-master kubectl get kj -n cross-domain ``` -任务运行遇到网络错误时,可以参考[这里](../reference/troubleshoot/networktroubleshoot.md)排查 + +任务运行遇到网络错误时,可以参考[这里](../reference/troubleshoot/networktroubleshoot.md)排查 \ No newline at end of file diff --git a/docs/deployment/Docker_deployment_kuscia/deploy_p2p_cn.md b/docs/deployment/Docker_deployment_kuscia/deploy_p2p_cn.md index 74536864..1c88ca94 100644 --- a/docs/deployment/Docker_deployment_kuscia/deploy_p2p_cn.md +++ b/docs/deployment/Docker_deployment_kuscia/deploy_p2p_cn.md @@ -16,14 +16,16 @@ 登录到安装 alice 的机器上,本文为叙述方便,假定节点 ID 为 alice ,对外可访问的 PORT 是 11080 。 -指定 Kuscia 使用的镜像版本,这里使用 0.9.0b0 版本 +指定 Kuscia 使用的镜像版本,这里使用 0.10.0b0 版本 ```bash -export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.9.0b0 +export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia:0.10.0b0 ``` + 指定 Secretflow 版本: + ```bash -# 使用的 Secretflow 镜像,这里使用 1.7.0b0 版本镜像 -export SECRETFLOW_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:1.7.0b0 +# 使用的 Secretflow 镜像,这里使用 1.8.0b0 版本镜像 +export SECRETFLOW_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:1.8.0b0 ``` 获取部署脚本,部署脚本会下载到当前目录: @@ -32,12 +34,15 @@ export SECRETFLOW_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretfl docker pull $KUSCIA_IMAGE && docker run --rm $KUSCIA_IMAGE cat /home/kuscia/scripts/deploy/kuscia.sh > kuscia.sh && chmod u+x kuscia.sh ``` -生成 alice 节点配置文件: +生成 alice 节点配置文件,kuscia init 参数请参考[kuscia 配置文件](../kuscia_config_cn.md#id3): + ```bash # --domain 参数传递的是节点 ID -docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode autonomy --domain "alice" > autonomy_alice.yaml +docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode autonomy --domain "alice" > autonomy_alice.yaml 2>&1 || autonomy_alice.yaml ``` +建议检查生成的文件,避免配置文件错误导致的部署启动问题。 + 启动节点,默认会在当前目录下创建 ${USER}-kuscia-autonomy-alice/data 目录用来存放 alice 的数据。部署节点需要使用 `kuscia.sh` 脚本并传入节点配置文件: ```bash @@ -47,26 +52,28 @@ docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode autonomy --domain "alice" # -m 或者 --memory-limit 参数给节点容器设置适当的内存限制。例如,'-m 4GiB 或 --memory-limit=4GiB' 表示限制最大内存 4GiB,'-m -1 或 --memory-limit=-1'表示没有限制,不设置默认 master 为 2GiB,lite 节点 4GiB,autonomy 节点 6GiB。 ./kuscia.sh start -c autonomy_alice.yaml -p 11080 -k 11081 ``` -> 注意事项:
-> - 如果多个 lite 节点部署在同一个物理机上,可以用 -p -k -g -q 参数指定下端口号(例如:./kuscia.sh start -c autonomy_alice.yaml -p 11080 -k 11081 -g 11082 -q 11083),防止出现端口冲突。
-> - 目前 kuscia.sh 脚本仅支持导入 Secretflow 镜像,scql、serving 以及其他自定义镜像请移步至[注册自定义算法镜像](../../development/register_custom_image.md)
-> - 如果节点之间的入口网络存在网关时,为了确保节点与 master 之间通信正常,需要网关符合一些要求,详情请参考[这里](../networkrequirements.md)
-> - alice、bob 节点默认使用 sqlite 作为存储,如果生产部署,需要配置链接到 mysql 数据库的连接串,具体配置可以参考[这里](../kuscia_config_cn.md#id3)
-> - 需要对合作方暴露的 Kuscia 端口,可参考 [Kuscia 端口介绍](../kuscia_ports_cn.md) - +:::{tip} +- 节点 ID 需要符合 RFC 1123 标签名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) +- 如果多个 lite 节点部署在同一个物理机上,可以用 -p -k -g -q 参数指定下端口号(例如:./kuscia.sh start -c autonomy_alice.yaml -p 11080 -k 11081 -g 11082 -q 11083),防止出现端口冲突。 +- 目前 kuscia.sh 脚本仅支持导入 Secretflow 镜像,scql、serving 以及其他自定义镜像请移步至[注册自定义算法镜像](../../development/register_custom_image.md) +- 如果节点之间的入口网络存在网关时,为了确保节点与 master 之间通信正常,需要网关符合一些要求,详情请参考[这里](../networkrequirements.md) +- alice、bob 节点默认使用 sqlite 作为存储,如果生产部署,需要配置链接到 mysql 数据库的连接串,具体配置可以参考[这里](../kuscia_config_cn.md#id3) +- 需要对合作方暴露的 Kuscia 端口,可参考 [Kuscia 端口介绍](../kuscia_ports_cn.md) +::: ### 部署 bob 节点 你可以选择在另一台机器上部署 bob 节点,详细步骤参考上述 alice 节点部署的流程,唯一不同的是在部署前准备参数时配置 bob 节点相关的参数。假定节点 ID 为 bob ,对外可访问的 PORT 是 21080 。 - ### 配置证书 + 在两个 Autonomy 节点建立通信之前,你需要先给这两个节点互换证书。 #### alice 颁发证书给 bob 准备 alice 的公钥,在 alice 节点的机器上,可以看到包含公钥的 crt 文件: + ```bash # [alice 机器] 将 domain.crt 从容器内部拷贝出来并重命名为 alice.domain.crt docker cp ${USER}-kuscia-autonomy-alice:/home/kuscia/var/certs/domain.crt alice.domain.crt @@ -134,28 +141,37 @@ docker exec -it ${USER}-kuscia-autonomy-bob scripts/deploy/join_to_host.sh bob a ``` #### 检查节点之间网络通信状态 + - 方法一: [alice 机器] 执行以下命令: + ```bash docker exec -it ${USER}-kuscia-autonomy-alice kubectl get cdr alice-bob ``` + [bob 机器] 执行以下命令: + ```bash docker exec -it ${USER}-kuscia-autonomy-bob kubectl get cdr bob-alice ``` -当 "READR" 列为 "True"时,说明 alice 和 bob 之间授权建立成功。 + +当 "READR" 列为 "True" 时,说明 alice 和 bob 之间授权建立成功。 - 方法二: [alice 机器] 执行以下命令: + ```bash docker exec -it ${USER}-kuscia-autonomy-alice kubectl get cdr alice-bob -o=jsonpath="{.status.tokenStatus.sourceTokens[*]}" ``` + [bob 机器] 执行以下命令: + ```bash docker exec -it ${USER}-kuscia-autonomy-bob kubectl get cdr bob-alice -o=jsonpath="{.status.tokenStatus.sourceTokens[*]}" ``` + 当命令执行成功得到返回结果时表示授权成功 授权失败,请参考[授权错误排查](../reference/troubleshoot/networkauthorizationcheck.md)文档 @@ -163,6 +179,7 @@ docker exec -it ${USER}-kuscia-autonomy-bob kubectl get cdr bob-alice -o=jsonpat 注意:如果节点之间的入口网络存在网关时,为了确保节点与节点之间通信正常,需要网关符合一些要求,详情请参考[这里](./networkrequirements.md) ### 准备测试数据 + - alice 节点准备测试数据 登录到安装 alice 的机器上,将默认的测试数据拷贝到之前部署目录的 ${USER}-kuscia-autonomy-alice/data 下 @@ -170,10 +187,13 @@ docker exec -it ${USER}-kuscia-autonomy-bob kubectl get cdr bob-alice -o=jsonpat ```bash docker pull $KUSCIA_IMAGE && docker run --rm $KUSCIA_IMAGE cat /home/kuscia/var/storage/data/alice.csv > ${USER}-kuscia-autonomy-alice/data/alice.csv ``` + 为 alice 的测试数据创建 domaindata + ```bash docker exec -it ${USER}-kuscia-autonomy-alice scripts/deploy/create_domaindata_alice_table.sh alice ``` + 为 alice 的测试数据创建 domaindatagrant ```bash @@ -184,6 +204,7 @@ docker exec -it ${USER}-kuscia-autonomy-alice curl -X POST 'https://127.0.0.1:80 "domaindata_id": "alice-table" }' --cacert /home/kuscia/var/certs/ca.crt --cert /home/kuscia/var/certs/ca.crt --key /home/kuscia/var/certs/ca.key ``` + - bob 节点准备测试数据 登录到安装 bob 的机器上,将默认的测试数据拷贝到之前部署目录的 ${USER}-kuscia-autonomy-alice/data 下 @@ -191,10 +212,13 @@ docker exec -it ${USER}-kuscia-autonomy-alice curl -X POST 'https://127.0.0.1:80 ```bash docker pull $KUSCIA_IMAGE && docker run --rm $KUSCIA_IMAGE cat /home/kuscia/var/storage/data/bob.csv > ${USER}-kuscia-autonomy-bob/data/bob.csv ``` + 为 bob 的测试数据创建 domaindata + ```bash docker exec -it ${USER}-kuscia-autonomy-bob scripts/deploy/create_domaindata_bob_table.sh bob ``` + 为 bob 的测试数据创建 domaindatagrant ```bash @@ -209,12 +233,15 @@ docker exec -it ${USER}-kuscia-autonomy-bob curl -X POST 'https://127.0.0.1:8082 ### 执行作业 创建并启动作业(两方 PSI 任务), 以 alice 节点机器上执行命令为例 + ```bash docker exec -it ${USER}-kuscia-autonomy-alice scripts/user/create_example_job.sh ``` 查看作业状态 + ```bash docker exec -it ${USER}-kuscia-autonomy-alice kubectl get kj -n cross-domain ``` + 任务运行遇到网络错误时,可以参考[这里](../reference/troubleshoot/networktroubleshoot.md)排查 diff --git a/docs/deployment/K8s_deployment_kuscia/K8s_master_lite_cn.md b/docs/deployment/K8s_deployment_kuscia/K8s_master_lite_cn.md index 7627a0ea..a9575428 100644 --- a/docs/deployment/K8s_deployment_kuscia/K8s_master_lite_cn.md +++ b/docs/deployment/K8s_deployment_kuscia/K8s_master_lite_cn.md @@ -38,10 +38,13 @@ kubectl create -f service.yaml ConfigMap 是用来配置 Kuscia 的配置文件,详细的配置文件介绍参考[Kuscia配置](../kuscia_config_cn.md) domainID、私钥以及 datastoreEndpoint 字段里的数据库连接串(user、password、host、database)需要替换成真实有效的信息,私钥可以通过命令 `docker run -it --rm secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia scripts/deploy/generate_rsa_key.sh` 生成 -> 注意:
-> - database 名称暂不支持 "-" 特殊字符
-> - 修改 Configmap 配置后,需执行 kubectl delete po {pod-name} -n {namespace} 重新拉起 Pod 生效
-> - 节点 ID 需要符合 DNS 子域名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + +:::{tip} + +- 修改 Configmap 配置后,需执行 kubectl delete po {pod-name} -n {namespace} 重新拉起 Pod 生效 +- 节点 ID 需要符合 RFC 1123 标签名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) + +::: 获取 [configmap.yaml](https://github.com/secretflow/kuscia/blob/main/hack/k8s/master/configmap.yaml) 文件,创建 Configmap;因为这里面涉及很多敏感配置,请在生产时务必重新配置,不使用默认配置。 @@ -84,9 +87,13 @@ kubectl create -f service.yaml ConfigMap 是用来配置 Kuscia 的配置文件,详细的配置文件介绍参考[Kuscia配置](../kuscia_config_cn.md) 部署 Configmap 需要提前在 Master 节点 Pod 内生成 domainID 以及 Token,并填写到 Configmap 的 domainID 和 liteDeployToken 字段中,私钥可以通过命令 `docker run -it --rm secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia scripts/deploy/generate_rsa_key.sh` 生成并填写到 domainKeyData 字段中 -> 注意:
-> - 修改 Configmap 配置后,需执行 kubectl delete po pod-name -n namespace 重新拉起 Pod 生效
-> - 节点 ID 需要符合 DNS 子域名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + +:::{tip} + +- 修改 Configmap 配置后,需执行 kubectl delete po pod-name -n namespace 重新拉起 Pod 生效 +- 节点 ID 需要符合 RFC 1123 标签名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) + +::: lite-bob 配置与 lite-alice 一样,下面以 Alice 为例: ```bash diff --git a/docs/deployment/K8s_deployment_kuscia/K8s_p2p_cn.md b/docs/deployment/K8s_deployment_kuscia/K8s_p2p_cn.md index e69a45ba..307febfe 100644 --- a/docs/deployment/K8s_deployment_kuscia/K8s_p2p_cn.md +++ b/docs/deployment/K8s_deployment_kuscia/K8s_p2p_cn.md @@ -38,10 +38,11 @@ kubectl create -f service.yaml ConfigMap 是用来配置 Kuscia 的配置文件,详细的配置文件介绍参考[Kuscia配置](../kuscia_config_cn.md) domainID、私钥以及 datastoreEndpoint 字段里的数据库连接串(user、password、host、database)需要替换成真实有效的信息,私钥可以通过命令 `docker run -it --rm secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia scripts/deploy/generate_rsa_key.sh`生成 -> 注意:
-> - database 名称暂不支持 "-" 特殊字符
-> - 修改 Configmap 配置后,需执行 kubectl delete po {pod-name} -n {namespace} 重新拉起 Pod 生效
-> - 节点 ID 需要符合 DNS 子域名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) + +:::{tip} +- 修改 Configmap 配置后,需执行 kubectl delete po {pod-name} -n {namespace} 重新拉起 Pod 生效 +- 节点 ID 需要符合 RFC 1123 标签名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) +::: 特殊说明:为了使 ServiceAccount 具有创建、查看、删除等资源权限,RunK 模式提供两种方式: - 方式一:在 Configmap 的 KubeconfigFile 字段配置具有同等权限的 Kubeconfig diff --git a/docs/deployment/deploy_check.md b/docs/deployment/deploy_check.md index f4b31623..141e39e9 100644 --- a/docs/deployment/deploy_check.md +++ b/docs/deployment/deploy_check.md @@ -6,6 +6,9 @@ Kuscia 对操作系统、Docker 版本、处理器型号等有一些要求,部 ## Docker 版本要求 我们推荐使用 Docker **20.10 或更高版本**。Docker 的安装请参考[官方文档](https://docs.docker.com/engine/install/),Docker 部署包下载参考[Docker软件包](https://download.docker.com/linux/centos/7/x86_64/stable/Packages/)。 +## Mysql 版本要求 +我们推荐使用 Mysql **8.0.23 或者更高版本**,防止由于小版本(如 8.0.20) Bug 导致的连接异常。 + ## 处理器要求 支持的处理器包括: - **x86_64** diff --git a/docs/deployment/kuscia_config_cn.md b/docs/deployment/kuscia_config_cn.md index d941190e..97d2002d 100644 --- a/docs/deployment/kuscia_config_cn.md +++ b/docs/deployment/kuscia_config_cn.md @@ -57,7 +57,7 @@ capacity: # agent 镜像配置 image: - pullPolicy: #使用镜像仓库|使用本地 + pullPolicy: #是否允许拉取远程镜像(remote)|仅使用本地已导入镜像(local) defaultRegistry: "" registries: - name: "" @@ -80,7 +80,7 @@ enableWorkloadApprove: false ### 配置项详解 - `mode`: 当前 Kuscia 节点部署模式 支持 Lite、Master、Autonomy(不区分大小写), 不同部署模式详情请参考[这里](../reference/architecture_cn) -- `domainID`: 当前 Kuscia 实例的 [节点 ID](../reference/concepts/domain_cn), 需要符合 DNS 子域名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names), 生产环境使用时建议将 domainID 设置为全局唯一,建议使用:公司名称-部门名称-节点名称,如: domainID: mycompany-secretflow-trainlite +- `domainID`: 当前 Kuscia 实例的 [节点 ID](../reference/concepts/domain_cn), 需要符合 RFC 1123 标签名规则要求,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names), 生产环境使用时建议将 domainID 设置为全局唯一,建议使用:公司名称-部门名称-节点名称,如: domainID: mycompany-secretflow-trainlite - `domainKeyData`: 节点私钥配置, 用于节点间的通信认证(通过 2 方的证书来生成通讯的身份令牌),节点应用的证书签发(为了加强通讯安全性,Kuscia 会给每一个任务引擎分配 MTLS 证书,不论引擎访问其他模块(包括外部),还是其他模块访问引擎,都走 MTLS 通讯,以免内部攻破引擎。)。可以通过命令 `docker run -it --rm secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia scripts/deploy/generate_rsa_key.sh` 生成 - `logLevel`: 日志级别 INFO、DEBUG、WARN,默认 INFO - `liteDeployToken`: 节点首次连接到 Master 时使用的是由 Master 颁发的一次性 Token 进行身份验证[获取Token](../deployment/deploy_master_lite_cn.md#lite-alice),该 Token 在节点成功部署后立即失效。在多机部署中,请保持该 Token 不变即可;若节点私钥遗失,必须在 Master 上删除相应节点的公钥并重新获取 Token 部署。详情请参考[私钥丢失如何重新部署](./../reference/troubleshoot/private_key_loss.md) @@ -95,17 +95,17 @@ enableWorkloadApprove: false - `memory`: 内存大小,如 8Gi - `pods`: pods 数,如 500 - `storage`: 磁盘容量,如 100Gi -- `image`: 节点镜像配置, 暂未实现 - - `pullPolicy`: 镜像策略,使用本地镜像仓库还是远程镜像仓库 - - `defaultRegistry`: 默认镜像 - - `registries`: 镜像仓库配置,类型数组 +- `image`: 节点镜像配置, 目前仅支持配置1个镜像仓库(更多请参考:[自定义镜像仓库](../reference/troubleshoot/custom_registry.md)) + - `pullPolicy`: [暂不支持] 镜像策略,使用本地镜像仓库还是远程镜像仓库;可选值有remote/local,不区分大小写,默认为local;当为remote时,如果发现本地镜像不存在,会根据registry账密自动拉取远程的镜像;如果为local时,镜像需要手动导入kuscia内,如果镜像没有导入kuscia,任务会启动失败。local模式因为不拉取远程镜像,安全性会更高,但会有易用性的损失,用户可结合业务场景自行选择。 + - `defaultRegistry`: 默认镜像仓库(对应registries中其中一个registry的name字段) + - `registries`: 镜像仓库配置。 - `name`: 镜像仓库名 - `endpoint`: 镜像仓库地址 - - `username`: 镜像仓库用户名 - - `password`: 镜像仓库密码 + - `username`: 镜像仓库用户名(公开仓库可不填) + - `password`: 镜像仓库密码(公开仓库可不填) - `datastoreEndpoint`: 数据库连接串,不填默认使用 sqlite。示例:`mysql://username:password@tcp(hostname:3306)/database-name`使用mysql数据库存储需要符合以下规范: - - database 数据库名称暂不支持 "-"。 - - 创建数据库和表 kine ,建表语句参考[kine](https://github.com/secretflow/kuscia/blob/main/hack/k8s/kine.sql)。 + - 提前创建好 Database。 + - 创建 kine 表,建表语句参考[kine](https://github.com/secretflow/kuscia/blob/main/hack/k8s/kine.sql)。 - 手动建表:如果机构建表是被管控的,或者提供的数据库账号没有建表权限,可以提前手动建立好数据表,kuscia 识别到数据表存在后,会自动跳过建表。 - 自动建表:如果提供的数据库账号有建表权限(账号具有`DDL+DML`权限),并且数据表不存在,kuscia 会尝试自动建表,如果创建失败 kuscia 会启动失败。 - 数据库账户对表中字段至少具有 select、insert、update、delete 操作权限。 @@ -121,21 +121,73 @@ enableWorkloadApprove: false - [Master 节点配置示例](https://github.com/secretflow/kuscia/tree/main/scripts/templates/kuscia-master.yaml) - [Autonomy 节点配置示例](https://github.com/secretflow/kuscia/tree/main/scripts/templates/kuscia-autonomy.yaml) +### 快速生成配置文件 +Kuscia 为您提供了快速生成 kuscia.yaml 文件的小工具,参数及示例如下: + +- `-e, --datastore-endpoint ` + - 描述:指定用于连接数据存储的数据库数据源名称(DSN)连接字符串。 + - 使用示例:`--datastore-endpoint "mysql://username:password@tcp(hostname:3306)/database-name"` + +- `-d, --domain ` + - 描述:设定必须遵守 DNS 子域命名规则的 Domain ID,详情请参考[这里](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names)。 + - 使用示例:`--domain "alice"` + +- `-f, --domain-key-file ` + - 描述:指定域的 RSA 私钥文件的路径。如果未提供,将生成新的域 RSA 密钥。 + - 使用示例:`--domain-key-file "/path/to/domain/key.pem"` + +- `--enable-workload-approve` + - 描述:设置后可自动批准工作负载的配置,只在 master 以及 autonomy 模式下才会生成该配置属性,默认为 false,使用该参数即表示该值为 true。 + - 使用示例:`--enable-workload-approve` + +- `-t, --lite-deploy-token ` + - 描述:用于验证连接到主服务器时由 Lite 客户端使用的部署令牌。 + - 使用示例:`--lite-deploy-token "abcdefg"` + +- `-l, --log-level ` + - 描述:设置日志记录级别。可接受的值有 INFO、DEBUG 和 WARN,默认为 INFO。 + - 使用示例:`--log-level "DEBUG"` + +- `-m, --master-endpoint ` + - 描述:指定 Lite 客户端应连接的主服务器端点。 + - 使用示例:`--master-endpoint "https://1.1.1.1:18080"` + +- `--mode ` + - 描述:设置域的部署模式。有效选项为 Master、Lite 和 Autonomy(不区分大小写)。 + - 使用示例:`--mode "Lite"` + +- `-p, --protocol ` + - 描述:指定用于 KusciaAPI 和网关的协议。选项包括 NOTLS、TLS 和 MTLS,不指定时默认为 MTLS。 + - 使用示例:`--protocol "TLS"` + +- `-r, --runtime ` + - 描述:定义要使用的域运行时。有效选项为 runc、runk 和 runp,默认为 runc。 + - 使用示例:`--runtime "runc"` + +Kuscia init 使用示例如下: +```bash +# 指定 Kuscia 使用的镜像版本,这里使用 latest 版本 +export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia + +# 命令执行后建议提前检查下生成的文件,避免配置文件错误导致的部署启动问题 +docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode lite --domain "alice" --master-endpoint "https://1.1.1.1:18080" --lite-deploy-token "abcdefg" > lite_alice.yaml 2>&1 || cat lite_alice.yaml +``` + ## 修改默认配置文件 -如果使用 [start_standalone.sh](https://github.com/secretflow/kuscia/blob/main/scripts/deploy/start_standalone.sh) 或者 [deploy.sh](https://github.com/secretflow/kuscia/blob/main/scripts/deploy/deploy.sh) 脚本部署的 kuscia,kuscia.yaml 文件路径默认是在以下位置(其他部署模式可以借鉴)。 +如果使用 [kuscia.sh](https://github.com/secretflow/kuscia/blob/main/scripts/deploy/kuscia.sh) 脚本部署的 Kuscia,kuscia.yaml 文件路径默认是在以下位置(其他部署模式可以借鉴)。 - 宿主机路径: - - master:\${PWD}/\${USER}-kuscia-master/kuscia.yaml - - lite:\${PWD}/\${USER}-kuscia-lite-domainID/kuscia.yaml - - autonomy:\${PWD}/\${USER}-kuscia-autonomy-domainID/kuscia.yaml + - master:{PWD}/{USER}-kuscia-master/kuscia.yaml + - lite:{PWD}/{USER}-kuscia-lite-domainID/kuscia.yaml + - autonomy:{PWD}/{USER}-kuscia-autonomy-domainID/kuscia.yaml - 容器内路径:/home/kuscia/etc/conf/kuscia.yaml 宿主机路径下修改 kuscia.yaml 配置后,重启容器 `docker restart ${container_name}` 生效。 > Tips:如果要修改 Protocol 字段,请确保对该字段有充足的理解,否则会导致 KusciaAPI 调用失败或者和其他节点的通讯异常。详情参考[Protocol 通信协议](../reference/troubleshoot/protocol_describe.md)。 ## 指定配置文件 -如果使用 [deploy.sh](https://github.com/secretflow/kuscia/blob/main/scripts/deploy/deploy.sh) 脚本部署的 Kuscia,可以指定配置文件,示例: +如果使用 [kuscia.sh](https://github.com/secretflow/kuscia/blob/main/scripts/deploy/kuscia.sh) 脚本部署的 Kuscia,可以指定配置文件,示例: ```bash # -c 参数传递的是指定的 Kuscia 配置文件路径。 -./deploy.sh autonomy -n alice -p 11080 -k 8082 -c kuscia-autonomy.yaml +./kuscia.sh start -c autonomy_alice.yaml -p 11080 -k 11081 ``` 其中,kuscia-autonomy.yaml 可参考 [配置示例](#configuration-example) diff --git a/docs/deployment/kuscia_monitor.md b/docs/deployment/kuscia_monitor.md index 541fbedd..6a1012cb 100644 --- a/docs/deployment/kuscia_monitor.md +++ b/docs/deployment/kuscia_monitor.md @@ -42,16 +42,15 @@ Kuscia 暴露的监控指标项 | 模块 |指标 | 类型 | 含义 | | -- | ---------------------- | --------------------- | ------------------------------------------------------------ | | CPU | node_cpu_seconds_total | Counter | CPU 总使用时间(可计算cpu使用率)| -| CPU | node_cpu_frequency_max_hertz | Gauge | 可统计CPU核数(无直接核数指标,可用lspu替代) | | MEM | node_memory_MemTotal_bytes | Gauge | 总内存字节数| | MEM | node_memory_MemAvailable_bytes | Gauge | 可用内存字节数(可计算内存使用率) | | MEM | process_virtual_memory_max_bytes | Gauge | 最大虚拟内存字节数 | | MEM | process_virtual_memory_bytes| Gauge | 当前虚拟内存字节数 | | DISK | node_disk_io_now | Counter | 磁盘 io 次数 | -| DISK | node_disk_io_time | Counter | 磁盘 io 时间 | +| DISK | node_disk_io_time_seconds_total | Counter | 磁盘 io 时间 | | DISK | node_disk_read_bytes_total | Counter | 磁盘读取总字节数 | | DISK | node_disk_read_time_seconds_total | Counter | 磁盘读取总时间 | -| DISK | node_disk_write_bytes_total | Counter | 磁盘写入总字节数 | +| DISK | node_disk_written_bytes_total | Counter | 磁盘写入总字节数 | | DISK | node_disk_write_time_seconds_total | Counter | 磁盘写入总时间 | | DISK | node_filesystem_avail_bytes | Gauge | 可用磁盘字节数 | | DISK | node_filesystem_size_bytes | Gauge | 总磁盘字节数 | @@ -70,16 +69,16 @@ Kuscia 暴露的监控指标项 | NET | node_netstat_Tcp_PassiveOpens | Gauge | 当前 TCP 处在passive_open 的总连接数 | | NET | node_sockstat_TCP_alloc| Gauge | 当前 TCP 处在 allocate 状态的总连接数 | | NET | node_sockstat_TCP_inuse| Gauge | 当前 TCP 处在inuse 状态的总连接数 | -| NET | avg_rtt | Gauge |tcp连接的流平均往返时延(Round Trip Tie) | -| NET | max_rtt | Gauge |tcp连接的流最大往返时延(Round Trip Tie) | -| NET | retrains | Counter | tcp重传次数 | -| NET | retrain_rate | Gauge | tcp重传率 (重传次数/总连接) | -| ENVOY | upstream_rq_total | Counter | 上游(envoy作为服务器端)请求总数 | -| ENVOY | upstream_cx_total | Counter | 上游(envoy作为服务器端))连接总数 | -| ENVOY | upstream_cx_tx_bytes_total | Counter | 上游(envoy作为服务器端)发送连接字节总数 | -| ENVOY | upstream_cx_rx_bytes_total | Counter | 上游(envoy作为服务器端)接收连接字节总数 | -| ENVOY | health_check.attempt | Counter | envoy 针对上游服务器集群健康检查次数 | -| ENVOY | health_check.failure | Counter | envoy 针对上游服务器集群立即失败的健康检查次数(如 HTTP 50 错误)以及网络故障导致的失败次数 | -| ENVOY | upstream_cx_connect_fail | Counter | 上游(envoy作为服务器端)总连接失败次数 | -| ENVOY | upstream_cx_connect_timeout | Counter | 上游(envoy作为服务器端)总连接超时次数 | -| ENVOY | upstream_rq_timeout | Counter | 上游(envoy作为服务器端)等待响应超时的总请求次数 | \ No newline at end of file +| NET | ss_rtt | Gauge |tcp连接的流平均往返时延(Round Trip Tie) | +| NET | ss_retrans | Counter | tcp重传次数 | +| NET | ss_retran_rate | Gauge | tcp重传率 (重传次数/总连接) | +| NET | ss_total_connections | Counter | 与各个Domain的 TCP 连接数 | +| ENVOY | envoy_cluster_upstream_rq_total | Counter | 上游(envoy作为服务器端)请求总数 | +| ENVOY | envoy_cluster_upstream_cx_total | Counter | 上游(envoy作为服务器端))连接总数 | +| ENVOY | envoy_cluster_upstream_cx_tx_bytes_total | Counter | 上游(envoy作为服务器端)发送连接字节总数 | +| ENVOY | envoy_cluster_upstream_cx_rx_bytes_total | Counter | 上游(envoy作为服务器端)接收连接字节总数 | +| ENVOY | envoy_cluster_health_check_attempt | Counter | envoy 针对上游服务器集群健康检查次数 | +| ENVOY | envoy_cluster_health_check_failure | Counter | envoy 针对上游服务器集群立即失败的健康检查次数(如 HTTP 50 错误)以及网络故障导致的失败次数 | +| ENVOY | envoy_cluster_upstream_cx_connect_fail | Counter | 上游(envoy作为服务器端)总连接失败次数 | +| ENVOY | envoy_cluster_upstream_cx_connect_timeout | Counter | 上游(envoy作为服务器端)总连接超时次数 | +| ENVOY | envoy_cluster_upstream_rq_timeout | Counter | 上游(envoy作为服务器端)等待响应超时的总请求次数 | \ No newline at end of file diff --git a/docs/development/build_kuscia_cn.md b/docs/development/build_kuscia_cn.md index fe44862e..8f333288 100644 --- a/docs/development/build_kuscia_cn.md +++ b/docs/development/build_kuscia_cn.md @@ -14,7 +14,7 @@ ### 安装 Protoc -[Protoc 安装教程](https://github.com/protocolbuffers/protobuf) +[Protoc 安装教程](https://github.com/protocolbuffers/protobuf/blob/main/README.md#protobuf-compiler-installation) ### 安装 Docker @@ -64,7 +64,7 @@ Kuscia 镜像的构建依赖 Kuscia-Envoy 镜像,Kuscia 提供默认的 [Kusci 在 Kuscia 项目根目录下: -执行`make image`命令,该命令将会使用 Docker 命令构建出 Kuscia 镜像。目前 Kuscia 暂时仅支持构建 linux/amd64 的 Anolis 镜像。 +执行`make image`命令,该命令将会使用 Docker 命令构建出 Kuscia 镜像。 如果你想依赖指定的 Kuscia-Envoy 镜像构建 Kuscia 镜像,你可以通过 `make image KUSCIA_ENVOY_IMAGE=${KUSCIA_ENVOY_IMAGE}` 来指定依赖镜像的名称。 diff --git a/docs/development/register_custom_image.md b/docs/development/register_custom_image.md index 8da9054f..48fa7dea 100644 --- a/docs/development/register_custom_image.md +++ b/docs/development/register_custom_image.md @@ -9,11 +9,10 @@ ### 获取工具脚本 ```shell -# 中心化组网部署模式 -docker cp ${USER}-kuscia-master:/home/kuscia/scripts/tools/register_app_image . +# 使用的 Kuscia 镜像,这里使用 latest 版本镜像 +export KUSCIA_IMAGE=secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/kuscia -# 点对点组网部署模式 -docker cp ${USER}-kuscia-autonomy-alice:/home/kuscia/scripts/tools/register_app_image . +docker pull $KUSCIA_IMAGE && docker run --rm $KUSCIA_IMAGE cat /home/kuscia/scripts/deploy/register_app_image.sh > register_app_image.sh && chmod u+x register_app_image.sh ``` ### 工具脚本介绍 @@ -26,61 +25,49 @@ docker cp ${USER}-kuscia-autonomy-alice:/home/kuscia/scripts/tools/register_app_ 查看工具脚本帮助信息 ```shell -./register_app_image/register_app_image.sh -h +./register_app_image.sh -h ``` 工具脚本支持的 Flag 参数含义如下: - `-h`:可选参数,查看工具脚本帮助信息 -- `-m`:必填参数,指定 Kuscia 的部署模式,支持`[center, p2p]`。中心化组网模式为`center`和点对点组网模式为`p2p` -- `-i`:必填参数,指定需要注册的自定义算法的 Docker 镜像,包含镜像名称和 TAG 信息。可以通过命令`docker images`查询。 镜像示例: `secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest` -- `-d`:可选参数,指定节点 Domain IDs,默认为:`alice,bob`。若需指定多节点 Domain ID,各节点 Domain ID 之间以`,`分隔 -- `-u`:可选参数,指定部署 Kuscia 的用户,默认为:`${USER}`。通过命令`echo ${USER}`可查看当前用户 -- `-n`:可选参数,指定自定义算法镜像相关的 Kuscia AppImage 名称。若不指定,则工具脚本将根据算法镜像名称生成对应的 AppImage 名称 +- `-c`:必填参数,指定需要注册的自定义算法的 Docker 容器名 +- `-i`:必填参数,指定需要注册的自定义算法的 Docker 容器镜像名 - `-f`:可选参数,指定自定义算法镜像相关的 Kuscia AppImage 模版文件。推荐在工具脚本同级目录下,以规则`{Kuscia AppImage 名称}.yaml`命名模版文件。否则必须通过该标志指定模版文件。 +- `--import`:可选参数,将自定义算法镜像导入到节点容器中时指定该参数。 ## 准备自定义算法镜像的 AppImage -你可以在工具目录`register_app_image`下获取 Secretflow 算法镜像的 AppImage 模版`secretflow-image.yaml`。若有需要,可参考 [AppImage](../reference/concepts/appimage_cn.md) 对模版进行修改。 +你可以在引擎官网获取到 AppImage 模版 -在该模版中,以下占位符不建议修改,这些占位符实际内容由工具脚本动态填充。 -- `{{APP_IMAGE_NAME}}`: 自定义算法镜像对应的 Kuscia AppImage 名称 -- `{{IMAGE_NAME}}`: 自定义算法镜像名称 -- `{{IMAGE_TAG}}`: 自定义算法镜像标签 +- `Secretflow` 引擎模版:[app_image.secretflow.yaml](https://github.com/secretflow/kuscia/blob/main/scripts/templates/app_image.secretflow.yaml) +- `Serving` 引擎模版:[app_image.serving.yaml](https://www.secretflow.org.cn/zh-CN/docs/serving/0.2.1b0/topics/deployment/serving_on_kuscia#appimage) +- `SCQL` 引擎模版:[app_image.scql.yaml](https://www.secretflow.org.cn/zh-CN/docs/scql/main/topics/deployment/run-scql-on-kuscia) +- 其他自定义算法镜像参考:[AppImage](https://www.secretflow.org.cn/zh-CN/docs/kuscia/main/reference/concepts/appimage_cn) -## 注册镜像 +## 加载自定义算法镜像到节点容器 -### 中心化组网部署模式 +## 注册镜像 -注册自定义算法镜像 +### 点对点模式 +- Autonomy 节点需要同时导入引擎镜像和注册 Appimage,下面以 root-kuscia-autonomy-alice 节点为例,其他 autonomy 节点也需要进行导入 ```shell -./register_app_image/register_app_image.sh -u {USER} -m center -n {APP_IMAGE_NAME} -f {APP_IMAGE_TEMPLATE_FILE} -i {IMAGE} - -# 示例: ${USER} 用户注册 secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest 镜像 -./register_app_image/register_app_image.sh -u ${USER} -m center -n secretflow-image -f ./register_app_image/secretflow-image.yaml -i secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest -=> register app image: secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest -... -=> app_image_name: secretflow-image +./register_app_image.sh -c root-kuscia-autonomy-alice -i secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/scql:latest -f appimage.yaml --import ``` -- 在自定义算法镜像注册完成后,可以获取算法镜像对应的 AppImage 资源名称: `app_image_name: secretflow-image` +### 中心化模式 +- Master 节点注册 Appimage 即可,下面以 root-kuscia-master 为例 -### 点对点组网部署模式 +```shell +./register_app_image.sh -c root-kuscia-master -i secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/scql:latest -f appimage.yaml +``` -注册自定义算法镜像 +- Lite 节点导入引擎镜像即可,下面以 root-kuscia-lite-alice 节点为例,其他 lite 节点也需要进行导入 ```shell -./register_app_image/register_app_image.sh -u {USER} -m p2p -n {APP_IMAGE_NAME} -i {IMAGE} - -# 示例: ${USER} 用户注册 secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest 镜像 -./register_app_image/register_app_image.sh -u ${USER} -m p2p -n secretflow-image -f ./register_app_image/secretflow-image.yaml -i secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest -=> register app image: secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest -... -=> app_image_name: secretflow-image +./register_app_image.sh -c root-kuscia-lite-alice -i secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/scql:latest --import ``` -- 在自定义算法镜像注册完成后,可以获取算法镜像对应的 AppImage 资源名称: `app_image_name: secretflow-image` - ## 使用自定义算法镜像运行作业 diff --git a/docs/getting_started/quickstart_cn.md b/docs/getting_started/quickstart_cn.md index c529a324..c9e9a362 100644 --- a/docs/getting_started/quickstart_cn.md +++ b/docs/getting_started/quickstart_cn.md @@ -124,7 +124,7 @@ docker exec -it ${USER}-kuscia-master-alice kubectl get kj -n cross-domain ## 作业状态 -如果作业执行成功,则 `kubectl get kj` 命令会显示类似下方的输出,Succeeded 表示成功状态: +如果作业执行成功,则 `kubectl get kj -n cross-domain` 命令会显示类似下方的输出,Succeeded 表示成功状态: ```bash NAME STARTTIME COMPLETIONTIME LASTRECONCILETIME PHASE diff --git a/docs/imgs/flight_do_get.png b/docs/imgs/flight_do_get.png new file mode 100644 index 0000000000000000000000000000000000000000..41e059f167d13b0e452b2adfd9818a36f1e9e4f4 GIT binary patch literal 52522 zcmeFYWmFzZ_qT}!cY+1?Bsc`u;7$U;-66QU+l{+haDqEG?jAI_yL)h*&U12}dH?5p znfWj?Yu0+#YFJHIQ`KFyYgg^-w>#vMoHz>N8$<{Q2oy;P5d{bc=tu|%$PRc|;L0&K zdo~0FvbmYC@Fz)OVbV`_*2ZQQMi3AZAqgsQs)~a+nHsUt(c@6?5(upb96xj1&!v0CAbkzUDgzaq-W6OV#G$65u$*r#2nc&k6&|NsY3?uS zT)(pHb`zm}jL}rc2Sw{32C#OeH@p4t3>1Ij#I?P4K2j-kW218@qNjL6s~Oke`uo1}Pht%w9~)o*TBd zhu}QtIzD1cb#G$db-!5|Ng@2+@dd=wi$B2}sxG`lu|a!?3L%9t)EyUpUBoQk7jy-E zkKTON*cpRPAWZmvG$K-ApOr;4n-H{a^$|(*E?oqKLaI#}9Y|SZQ7=K$U*eHnm!iNf zo!>r?fO@NR>qa*wssH_U72y>jwm`vWN*Nj7WT+}7-?v6KfT7R3H7m`b zm}$+T#YqVW*piWS&`u%24Sal;t)sV~CHYsA|MegXU&vimHw&4C?H$(SJKZ z%+d8^tHX;(483mCn*zSkC0;#3V1%a+C=AdDQ0{7#%yFG0$svSRC-W{U#=&2mg=+9k zCw4(Uftx2|Esr9Aq*)@m*IS1iuYsGYRB417zp??hG3d*i<9$oeQXMKaq*<)Rt$81Bf9Kcb(Hqg9NtZ&6ZG`X(pYGNP zorzOF^A~(jNmvO1M5uGb#rYM#)f$oC9*5`8kryI}_PD2~C-L@uayclQ4y2W5@wWY= zWC+s|Xq$6*c?SAV2R6OF5LuY1XL77C1s#}kogSLLG-t1TNnu2M@S@=rIz{kW(NFB( zB_kU9G=9U2_uJM*QAK0-x75X*LqhfAwn0vZ-R;D-L0N)ATRSm>uK0+13*m+VLq_%? zAQF$<7{s;#0+Y-1kRW`_iFTpHmK3i>XC@anCjAwoU`SO>JRkan3@(~tz-$Y1(r-yP zlay$HXNxs7`^y(xwvU$MF>kSqvSUoixgke{3crc&K{>ork5tZaA6LKp;P$FDus)|^ z!U|dUAerAdl1on}Jfc_EOqH6t*I18A6<;}IS{LPvd$mKoO6~|tu4kVU+S!kXlTg0P z_4c&~P8*C))M=ObdCe`#Bf?6cH`O?NAB?ykny)aP^Z>aG)oZLFcpms*gx$5)DY08Y zO3bT}dJ#q!#sbWmke?yaAcBwwP$7uE$E3#!GsIBBj`A)>UqVxX|9f(6WNolRtV70> z*uiH-6;?%iB_t(jMXZt$MOwwhGAE(qrUmIb?P{F))Fa_YvR{Kg2Zje02T`_4w%F|$ zMbq@9&vPjis^8ha+m3%T#5E*5lzcomJ$WbMT3%$l}1I zIK3&pDb6j_E$Ti~EO&k!%FL|RV?jY3`xyC{xlG)@e7&@DV$J-=OwoAJG`Rd+EUwh7 zY;ZEYNG(sTz*XW`v2h`e+OSwyS?_dezEZZM$YG>%1G|=cyi>SS<~8Q=@Se4`P%I5}z8y6!o}kXP7t82&41Pe}MPbDEvbps%n40V4TybeSXWyjW zw8B!1W5Nm<_j83EM-RMux7fGx=JRh21&p+V&kEZWm5?qW6U5 z+&Ph%k>vdmk@OVs6fNQ)@h_3!O%X?ZE^JOE9zNR{=SaKEUCrG`Vj~38Oa~by{{VJ@8vyMEq zTD_jA&wZ8K5aqRFvfh8Scrbe~b|!zby0Q93m$(C^2-)S0DgFT362@(;(b$mjv0f{J z9YQQD5$gTGwceOv7Gj@Yx1Y3Mny`aVtk6s_FKQk92x1xPG2$=yG-9Xs>FC7xZRC%r z{Q=#ZUu>JmTgZ*Y(j)T3s71=7Jl<@hQ$>8m^N$R{tNHq)+w{=X)iXK8 zfQRbZl#rQ$y`20+^s(p`{~Q-{4E6>!=+SP%T2yM+^BZGPA8%D0BNiU>K!>=eo1>w z=gDjbAvdY}lS_Up?W!irvq#Ubxsg+c7 z2S9eQ=mB$IozX3gD6^I8!9Y%CbTs{WDSpY*^{eZ?t9TFh2I2YqU_l%bO@jm>Rd4Lf zJATC(nf+KXDdz$7c^Ea%HipL{>)6FX?^uC%XN*N}_fk@MuRdivWrHitQoCz=e`@0d z;ykesWsRn-aCUq2E#b=>R~uVp1pXLGp5|P%yY`#6wC~zEN|jDYW4&o`W%#8$`e5!g z^ceJt8fk~lo(KxX} zSyb0BQ&g*u0GH`5h8+(sGCp=a=6?;Ih@2$SaSC;=KM7rRtRE^V{@QSUVl-`9*V1G# zcFQ+=4SQNvVOu=kB=4=W?^UVO_A>2z3EhAuMr^^`!~5k|$yEgb>QA(!De}6XUz-mC z9Lui0U(t(`M}A@CR7~UT#!bd$ktafKc)FS#Y4nI7&(>^@3TljZ&t(?cJ&ilU) zfOij~uhL<;VCiVbF!H|FiWJE7C5Li8+q~1+T!=x#lo|5`q7P0ZRY_x6S%~+*IXnbp zuo(mla0Utdya9e7AfUcM{e1;G@*DKu=a3!03l*h7N)Ql&5RxJvzqmjiWgslcOuzKE zC)Sc9n8mPZNYtt={0yp9RHK#7(NTZMj_iY&$U||pBDwqXji*C_ zYu^uE)-mJ6G1t~i6XO%U8@f~blOK2Kr#I;u7WU{eA7MlU|9R-6za`!TqlWpwgZ_C? zkggX+l0uRH<3LaH2{6#)<$m|C65xLRf7}fG`TzX=f`ed_pJ!V-wCoE(jxTw7gGyUp z$SRkwd&?Xe!<*R@UR4Z{L6Jw3!(6(jkfX%(^$u*UN-vX`Eio(R?WMSQzg{E^c)gxh zppM_fEQO4(f(Hp^ zqmQ2&W$TU{h_LS8DBOrEJ8uhp_n=-T!LRDWaE79bKqUVZ4YQ|sN&gB19TH>U_3xJA z6J^TW42MqhPoky{R8kaDWf}!cqr%Z6OU>x2G6D<=mUO=>pxsa1*Qqn6y+6*j0|s;} z%g2lH*Vw0b7P6#@UW|_37R&YAvEEQAcHV;I6afa$X`$Vu1n!onl}Z4{hP zn2`tY|Fj5FC|d*x&c5Ei@p~?|Jh7r~ZjcrQxtzGF|n&sgq5+t{zFh zt9p96S>-~!?}R%A|9W04Rw{YaXbt_#kqG)?&DEO4J)Lzs*L&PmC!1AhG>rDDHC~J* z+ahlE5LnC9>3BcoGsQ-x?WD4qA3fce%$2HTEeO0kVsVtqg$nXQm8euW-Jss!dAC2Y zuYz~$9dGY;Ni6R#RV;c#w-=k<4z+{eB{*>~_WXu+GMrZ`VNtHDh0)3kp6(qRHY0iF zQa^k*RxFT#H?rN$;F$j@FYk~AJiKg)dR;Bw%j4Gg-DArg@OZ}|G<{n%2wJrN@o3R{ zFv?~3!>Ih=#0<~t5N7l1+s$09bP?|;bnS)}Zq8NzNl57XxkYRFD7Er4tY5n?77fcz z!nfIZtE?dYo2B~tF@7GkDB|6@s($vm`zzYT2oBVEADPqw1|J9mt`o+W*`m+bC11Yh zWw;$MUvDSrP`(ObCNyY#3<$yMSMLsbfY}^7aN5tuRWDQD)>7ktd8~P)EHVG-1NhxH zA7U9dG^}N7oQ@;wOUmDTMZzgIUcYF?qjJGwG_>%p zFx{*>c-4B<aTwUbQkYa)YhQgyqRd$V-+aN7Q}$$@L6^>pfcMuxqd z`3*7c)_Ar`^Xl7jv!6R>g#J4M&#HQ@ZjKJ^)-GEz8Ta~;j~iNJjtxt8Wlxts(M~u} zC!}uA`%jNAhTTOUtr!MktZv~nZ4amBSBDapr-^oMn$&8#k0W`AYZdbAo%=VQaIhey zC*$P#Pw0}S;;QwyP|#E(d6V*`lVD~>?6q3&JV|(Or8L^TTEm`?YUCGQo}GQM${)*r z@l-^wz&I`0ikE1&7HB!mv(1z(iu!^Maf`=uqxeltG93My3sX5NtmCNpc1HD#@{YY< zo>ZFm3w%%baGZon&6fs2-b?)fK`3aom{d~Wn}qkrdeCqyrY4#5%;@!D%giv;Psz{2 ze9yO=XpNhX&Fevsog$O)&N{Zw#U=Mm7e6xjSYN}T%$eO;bOy!@-F!Nw)(6qCM^zs42ki`ykXU= zonE7UO1;5q-xy@WreBauLemCfXn$09zg>4f8lq*i6L7-S9PfX__EjGl+3oT+Yr16C zrsVPOVRlDwI9-{QWfCiYCjx(cFwK~JNr&y%9UGd-H&)Y)CNM+ny7jI^xvU_=LBamv zda>fDvKtjm{GLrgle{-e?CjPnM}jT< zdb3&_bZ6kknYNebwDCKa*(@(^t9+VyhUR#ucH4epcm1=)MuucvzTte|v+iz_(R4MZ zF}{Yt$#4&7|G~I^IRCv(l!-oT+V$F>3viAL3NtS4y*(Msn~pR%&WmL&THa5VousW* z5yYMiWZ|B8d~SwJZ!7db<3JCyRN8oL56w25Lb$I74*C}Dt>-YPRS%#noPrICHl0oy zcONJ(Qz2p0>%JcMxt8X7t_Qv=UzX?0wj{;VL4a8dahed#uo#T(^*y|7E7$qizP}s3 zIh0g^ev@kIg35cgri;1#iRbxN*#F^>JB`P=K1{iM$;y|sW%<3z%5a1VM%d+QFmdW@ zP?zG;R-n#74`0aK)5S1XxwoR6W=oc<@h}T5<`!{>Xv62+0;@Zv(o;l|q(N-&_M=v3 zY!&J!S{EoPB!0a3o7hBcE7u74xOo zoR30j%WF`Hn!q=-%5*hk!sXS+4Xgag6I?vtm_m*74XPz~+>bARcEM{pP~eifua4Cb z`1b8u_9L<9tC$PRCuAV3~jujV2-Oqxxyc_J~e!*eD_rgkJE7^rD4#Q=1fZqGADDVNcA;7$X$ha`e zE8RV7@d~~d@CHCIc%!h#Vb$rk>Ssqp0_qPNd3k;YR=3WcLzGTZS1>lM@{fF?=hLk? zd6dM+77|rk;>urR0>c?!SKI7$KOXPHE;T;kG+4FvPP)zM(OYf}h%a4P_;Hwy8~MJv zdUf*-eB0lg!lTS}ZiM5l%$)^wRF8?f7&s`M~26Wg9dYiFlv81 z%43Zh!6@mx(UY_+f^wR2w*CN#t9nvml9llqehJI}A@Zv8yqy-z|LC<|BeaelZ9_*` z{wni*O)-pseM%DYtsdEr%qO)oiU-fP2*z1py;d9E=Uet{dyhu+nQ|?254E>{cC^3- zV!7$cX_)l+roCjD6muA_>)n!U!gAYr&UcLl-$jW2LX~R6K|yrS1A!eEBc>4Zl@?cf zWTYKjogav0h4OZH{289pdQ(?3Gu=?IiBMqhL9_bqddB^f@tg#S5{r)i@$nbhs9jVur!NA| z!0mgOyI;5Rn|88dZ=*_NM;6AoW*^MEbnt68?3@RwmX~@ghZDtvOEcEbAKmHvRkY_9)gZXUo>g`GBiu(3 z1e`)-FB`yaSL*y~H*S6SyG?9q;7i#Q7Jl?it#qTl!d>s5uGf)5Qks zns$_>XlAmyqvhoE~UhZ+Xobk<|(s^1*Sg3Gr zhh(LO=}!djUM*DXj4^?i z-o|0O$PrilS9JcWT6mnmZaA56;(EUDt1ff;>x&b~>ju0qGUY}0lg8!7@BFuGFiHI8 z!rv*(C7hS*(s}A_*8Mu(^N_n33U3n{jR*fA_i=xphIhShpI?G!RRmUAwHubWNjWDo zf7u8$tkA3JIjlG(kvU6$(=;ZLHAoIMmMlWK0g3KqU9;M!Vcm*zkjr4#(_S-r@Oo~udACHm81BLwRwg;(M}b-}8HV>}g>YgXJp>0?by zej+hioEoz=JGWC>8DAZ+*$AwFQ7hT~GFHXH!Gm`>;-il}QlKj;!2iuQ?vrw3|8f1K zu@L5^a}fR`tol#v>j!-`L^_^Vfx4D6Z+7hl*2llGVs__?^w!NY!w;?o{8#CN?tNf2=tyT*D%;vzp8>-~SXPk<+FVD8+yx^5D6>TLoy8LGyw}h;MJz>cFvL55!&v)WCYqM%K{2}25V}C{e zY7(Ru&i+@j8QhM}(2|T69ey|AQNwQ}oB|AKraZ6b2Q7E}0{vte;CV;=APtfj3Y$%^ zDG)2#+=|?Fs>qc`YWzC)wi(d8a#f9)^}2?Al?+m)qdss&4v=p-@!8Hf>_!Wx!c{mf zm2ZH%SkQMcaP#^{^Srv>yfhCuu4n7Cn`?5pdUTKlAMJWEo;DpCH{`y<>Iiwp2`=1U zeQ?zDa#oI1taztb@G5OfrV5B7QJu0&F+r=R(8D9GCoE%&h1s*06l)J}okdVIT^=aGa89-fZ$9kdG<=uIm!evxFjvhDb8J(sdI@?sx)0kcRWupSlK{pC%Yeo2 zIcRMw#wuLY^|8M%g22zQL%vx{sj2{th`X!N@)sDTuk>i>^i_Fn+T{4->@49E_`oB_m zy}0yPlkkm8M~wjVAU?YXRftZujQubIQL&IWhLpD%RQ$dZ~y= z8)y&?$3H+jVMZ?Iz`Yx*C5@Kka8rj+ArlpZLCg@OMh-_Jie-^V6)bqvK6sXB+IRd2 z1;eua{H^$9z8tFvobzv7Wk>=9i$~`6)2jc3o3Q}MN`B2CiUau@qy3+U?-9T@lRYiV z-~1DC2`a!)62^7q5B!r^MSB7TH5DUD2LDSv|EW_V6bM-qz^~Q**?M&pP;gz>_?7OT zq*M|k+C~O3iTq!!Ujqd{+SpYhfZq5Ytd{g0K;9(R6%7B``T##rFh9Ff3~r*^?7li;9)_N?O)xJ2hbGiK@cqa2j8661-yuwkYIa~+T^&X=7`K)>(tZd z9wHj)YQQ@EcZNWpZTIb{ET2{}Bh~C)u+`Z1|F6JU{WSk*1eBVCsEGQt{yOEag_z3K zUqWPxVh<`K;4L)U2W3URl-K(oSC<5AvVVm&0)W#e34ltgN$A!v&C5Ij4-pec)EHfy z9&#hz2QR+5<6SmqdN}*MDR98%*HQiIa5?58L0U9@LR=o==8M`JK9t|I&G0?slXjoA z8t`kkdtT-ls0O3`$B}#!@{2> zBT(`{;e~Yg3>u?R95e2D(=TI5I4%7@4Nwq9!51*+_qBdQf1}I)+QK^-z@P#N#6_|G z*GUGP`+gw><{4tyH&Rn9A}{Z0#JH0BGn-!0ppuCo_D6#z^qfWP0Z#%h`SVa*@S zVzVz|1_-Ja^@54?Md{Br4`(YH7~5{Uo}VAA0lJ%c3{XAE?T;xO)+-i^mJM1kHA+~N>w-j_Md~PdnRBqN=U$Nk?i^M zbR*~TJKPSbs~O{8yuaL!EBS^X?=oGiRPS=GU!ss7O7MXK5f0^zp>^Axu}kf^yT}F# z$I{xyQPW}h^|&w^eh#*t-LDajMd1IeG#5w$qNC6gFG+nC3s{G{v z*Z^uhy;m;Xo7_t6^oLulHTzlWcsS!Ut)#MzUcrooDCcpr>^#%#Y^~Ac@IB0FfGl`U zmSfp)iYn%8gkyDXT1CeaKu)$3JkBRaFlx2t8g*Z{MMyS?UYW|s-9W4x?n zF(gf3pH<35ldhVmQuEtJ!&Q5EW(*d$OVj=)p+Lkz>g?OUr{WCER&nagNMTo4#|%s* zp==iavfJgJ9su;~lq$5BQ3W1tXeNJiMt9(Sen-*s_g!zrD=%6uC#9XHK8w${-mDx3 zWJLv0#k3sER-DoqsSe+oPvitnmubw3b}xwWGNQiRA2OPB(yElF0|D-s-1z;aeyg1) zxQ>N|80vgR!@7>$a$ydLB8=ka;Yyw3nenB!52j0gTaax<;EvqX7=b&$YaUFMXKd=L?e> zq8K;*M9%3%HXV>wIS{A-S?5@=)Cg?3OmT4>G3PTS=plc)9-^&qh{Jc4m7WdxBYSXT zM)xukX~-Jk_;SM7xDs;N1kBaET^!V?7Wrz zu8@C}Y!GSt<7U%nCz720Br zq1=>{Cgj8M8d?;aG#4rSpX$CU?IQ9T^dXxJ0_s6r?)5K`NkzgxURi^guC`f%P^frFfS--H_{}cd2i|g~R1Nj>$0k*tZsT(;S$*3cPHK30Xn(1>S#clh?S8rf?7a$0o_B%) zyawg`qH`bV%1|F0jcKyWKWGsNw%x3*DJ5Ytql**IAR-Uy{l_i28-7+yA6(#F`< z4Cd^s4mE%4=jXE*fS)Kt=e(Y%FsH9~X}kQYQ$VD^nj~_9e~a1i;0tbG^=F->&s>p6 zw5LGR)(s4pNSn*ftH%qmNW|WcwkFJ?kLy-aca@}lU!^sDJ-_MbGWSv8mb|5_k^_9{ z;!K;TJ57MWRgy3o-;^vTtfxU13>E<5}8EpCbxE}{;vhcQr(>Gy}?dLZ8 zJl1^S7DBOU({%%xTh>r0?vX9M9*!HD2Vboy^54Tawr<5rYu@R&ZYLByC3;2H5vb@N zL`buEMw-8ab}T&JH^0;gSTCt)I~bD9${|SyTzilWAdM6r4NycOtA+6i|5w>7z>F^W zT2NnlkdOd_{yTI|DB*#2>JbIM?Q9CvpzKF?MqaBb_5>Q2>%n;GC-JTgV{Ds4`GLRW zJ}WLVp5^_#5BsQk=h2Z9RAlHAG57J387~SMB*5NGrV{{3KHTi+1sGpF0LSt4lO7w1 zZ&b55%->_AKOEIWVX>)pqLbzcXH*5nGC=vUu-xu?x~%b%qd>X?p1j%Z2v7y&0;=lK zZ38qYaTsoOwS>D2%ZyqOYWu`HegFvPgrQ7n!vU5D~G1B(QI*#cRq8v5G43`4$g-&BbE@tu6WTD=;)!U^bc2uyOUy5Rl{$gI(PlI_tWi1 zZD#}m-syu5(cR!V4#G{>`4ai*Or96c>?$d!A=gDUOXcNx&jYYpK-^2x^Wi~P>7Ww( zF}~T#N^%ihLEY9(K-Jp_AebG^75W3G!(FY>BEu~2UG z75>L(8m{v`Q|zZcUGU|-8x zrKD6?O64_%NO|SB_bF+(L19LiVZb%> zXk0VZ%C{8IkES!c7h7oS;Y?)2clwYb$+}cM)m!+wBi&t>fGg(q_5UW=K~9aq29_iz ztO_DWZZ+-Sz-6Ew!0Pnfj;KYba@rQ5G(gDJhAGd_+!B964+1NbLuI+2a3z_zXkm=n zh;eR6NfQ_Z_9T2z*Kq4&r~!FT`PK&s7=dPWDxnl;@H)dn;GF#VSZsC!=#9QHc=R1( zT~D#Hc+(}64(HRu+lVHMBh_L@NoBuu5w1seZ1aPZRYvb3ve#g+7W@yZJjkcM7ox>u zsi+SfKp$`Rvg3$fAX=LPEbH%{HG_G6VEa*<`kkLVIuHi0mVyC(#WVH>)LL)3$c;oo zP>hbo9I~d%L}GP(e%^|=Y2VTTk>2kfU4WLCqM_Rn39-dJlS=j)BLkipD#s7f({PP@ zsE?MRCS(gcCFteo=LW0aVwjb8rpjb6wov$mzAPmvHM$K93>RJNo*<=IA`zLtXvsnQ zA=z?ZrvQvfiy*!v8i)SO&N@Trw{vCf04GlOti%d}Z8T^4>s@>IZSMA5+O*S8?JiG& z2#|D*M^+E_hZQ^xnnR(EG5j0!LGV>J*{{SIiQlneL5E!P>A;l>b9RNWe@mZ$+DQA= zBmU*5SWExaYuj*%ZS_^OWL!l#?3&-Dd+6X{S#Oi@Ow?zrB^m8)vWCtT`P1+|Ygz&a0M55v2S zjC;K9xXoEQ&>yCB@sWw#qEWu9B0>o^%eWefL)nd+;SXHd7t}r6e{^_TH}n?BIZJ@( z3X?Um$?r~xBNA`L4o7PFrl%XZ*P-7hNF3usRpk0>$13AZd2;Ege&s1#KREXu(thd( z1!^puzYXkGRe5gu*7k6BM~1jEGBNKQpAaPm`y{RRh2PAvx!Vx6F7`XNfGt-s|ca z-KeqiY_Q@8(`7bZ;nJn4M%pGA{UTrTK~R-S3HXMbx386fVGX{!A8RWeZ0jO$HqgeH zmq`>Up^YOua*@`RrQOa)R<(XdFniA)t_156jppmMzQ~~LL4^BP1nw(CMjB>NP@8u^ z1_cQzf(iwF-YfVf3?oWUR~XZ8)i&V<97v2`%3w=81S74t1eKcJPgf`S@u~##2{`A&_tM()(IB5N6R;?)Hka;5 zAMNjuPOm$t(a{O)9t77Tkk+JY6hLn^pn9(DwokR28G>YK1=%;T5If?9E(|k6Jy{R` z`E*69l`BX$Vpaz|)uOBWB+sHnMCT$(JG*oWgTt_N*rK|p2gVce^PsVaD?;gFAVK+P zp6J?L5@^cQv9~wrenV)Onp*~j^l@$-2Uas|FC5ntS^(ca5W5`l@A-l9oAqTUDD{_; z`(OvK1tSN{UtwT$uci7VcPsR=-dDC%n!i=%hMnY9_w(fwLJq@;sug6y$ciwOK*Ep` zeC?vQUY2|!GA96g3WB~c(T$q>*x_c5vi<9DV;~KZcO%TjBF~RWSl6ZGlj@|Lpe>&_ zyzD`@0r9{tu%mPMtoCy-mEDh#LLYa1dw7&c;lE_9{F7lB;RaBb%e|$g0$|DeMj8ae4AbkN2|8xjOY9gjd#q-Dxw>W$){@rs;jY~K-9 z-hB;T?ZCR$ybx7G6+I`@z+4mj6q37+Km@cC-(ugAI=aYDpZ_AV1F%rNek6TM&Q$}j znE0CFus=(kHz5~U1s1E%B{rc6xJw`s-&sct>B6X~vB9>(GZ8CxE-J=53uu=mZ&IPxR!{ zu5AU@G~ANekV6Y4?CMpg(K+@EiF?jDCVCX1?Mf!uQQQw6?xa~emDl_Z>&a2pgB{U2 zgV1wJ!R-oSs$_QLi<1Iym{vBI<&|5*4+H2G?H$36$+c*A9cYldf$xqbGLpZ2^SXq* zr}7Ma#PF+yo)J3rl1Z%%K4ra(F)wfITk(GJq``bg<6TMJdgfRaCqL>QLb3Fgw*)hW+h(mpQ%mEi&P`STYISs}8p*M&?}hJ#`sr&n zW|-hQ`uNjlPEAEVC4~Mj*4ZBMQNCmTX#A#8eAlWn4MMOr7T+doOvZ|!Upx#eq(w

+@=lq5h6eLCycYrY6P?li>q=F!H5Vp6bFpTpV|7v-&(fgBb-%&ZP*b5w zT?_*irP7{xO5GL>^UvHr#&xQ0^)XJDn;Xn$E%Yt2yDRSs>u4`;&}#yxzG5x&$gu@un2=HE3$lB6_~tZ!>XM0?UDIw8MTQu<1) z!+^-dPnfI*VIU`V{91UGF6SkxBS{++bu{&Nr;;jE*T zS$=gAF_5%q1SM|)PP-KaF~dY>Fi&}KAx?Ky(T9!SFz1&HFY&Zs?1fqbwA#&oDGgN?NT7Q9fY@7P?GL58S7 z3C!=jD?rLvGQf{YJ-I2|i9rBeadL zTO&=fw>c^y;n1{I{^&-&7Ubv*q}Gr1SxR&|^V%+V^Ad{$%dt^$=J zq~;eMr=#Q_RH1)m-I79?N$PG5#Msg({H#`ZU;Del*W{4L#p)oN2Bpa#IjDr3U$Hn8f z9YOUk&kq_k#v`T1BdPnU^a+0}euxo1t4ejsn_7tUXjF7i`%;28;!X9M&qHNwk$0O( zI4Ts#t(;R4nK-fqLVuE@|FA+S3;?7^u>~Vi{8J+Q{#&HGS`eYa{HLZ8|69~l7|HJX zGi?7={R9tw%Zjb+pF{pw0$?P69i;icHI*;+MNEHoIRBM!qTPVOQLK>tLjEsJ<+tMa zZkq6Y_@A-;Z*zIXfGJIxV-EAgz`%C!Y|2GKFdHxCT3l;J0CLuLyMa`qRFXIitloe;&~z7_K&#T!BAxKw z=;~m)Cc|mr-7E{%I^c9|x1WK4qznL-%K1_WLho+wuMUqlqXg=KX)q4}8Y`X?uz}FF zG$rQI9j(+#)sQHc*VC0s8mmbpE!Gq0`AfT(C#@RG^{giKWv8l4(IP;<&DtL?!r06v zdjR&2*VL}B75C#@ZI?5ozrDR@wHTdkt_8b$}o*@zpLnEn>g-J$s0DvwZCfwV6OuGO_xgvn zEe?Hvm@rJ)y|w+%&;rv?KdNX$r?7@Rn%0*}I#IR3YT2UG@73o`Wq{ZNiF)#B%mzVP zHl44W)&p^yJ`(8{3kRdhftCSEW7~`B{{zU5m@|mtGp5OKh(#gfWZLnG0}DK!`D2-R zeQI*pS4iT>+3X{*t_3ph8OX>2&fKr2RV>feI>nj-PL`WBk{Gls03sNru7i4iXw`b9 z`~WBwnk_&Q^L%;JL2>eSWQr;jP0{Ljwy@=VwGq4V54;kM1}k3>T)7h*swH=8Po(Q=p&JwZE}fI0&BwWW2- zc^~_}KXPn4kn(*v^*!#8U}7j)rX9K6Yb2G;>9qA`A(+I=^y&Vp1`u$X1x0=ap%5Ga zyRv$dEYBISa6COgi;@A$KRrL3S_!???6D&O(rd8giaPw=e*q~nj&XvQHN9cD&F(Yu z5R^Y}Om7(Nym)w!4|B9y6=A^G_$+!R@O+f)Uu2Y!a-(*tiu7 z^>PxHW^2Rp*c>CtM6GLz1%>4TFw+(wC6%qOu?t?thVSvL8*tmTpDt)JU2+^xmgeto zPEV=Z*4hD&v(QdHFH=*4`Zf* zqiT|9reA=bBCvSUe0nlU!C%$ai{vGUFJBo$?f(l<0Gc2}aU=7j@lzV*`|QLTPr#d~ zDo&+bQ&nm;#@TK??-^%ZCtS`t;hG`IS@#FGh-i;Z`}LZK8Jg#SG{w)~Tmho_WKtO> zibNKzLG-peKS}!l_QIe)vTmWyoQ94wXPW*3@5!f=N*R$F3LOzzOwbko#$+-HhMOv!nfplzka}&Al-E{0Cl)9kRkxfeZeV8SGx^+` zu6r+TH^LYfKEe?h%G7Q-Wd#>z{CM~+KGb2weUf3n@Ej$<3boOLf*Hf47A|kQKhS*8 zWO1b}|BnY1)CXKG)@GjS9cjlmlo+2^pc{Tt!Yr?QvzLihmX6--8laP`K8-<8t9SVI zmAd5NfpBKgI;y@fagJD%@&)W%Y!CmIS?&Fk0r6~+ia@)4zypkmL<-#pEW-0XkkEG6 zy)kBaLncGsGioMW=2|%?o$ze1Yal*``qMsxWw04 zCr;v7i$!~hwgGsy;ZelH-U@jEM$a*wwAtCmORa;S^_ChO2!hY>I+~!WAi#E~JZU-u z2mJwmMGs`iwakz$I9~El({!~HyhBE135H5|Q5b2wH0AXpcCR6VT8 zRYZG=FM(m>ZZIAp(UlOR0%b-XV6(jaW=NJ7%9TkMu$WNaD{yDoMr;~;?QiaNu7l&inD*C^6a4m?!%b^3jFT}649-pkq`Y3tX@%n!Wd zV(XsOMGtGg1*-#z6#ax4{=qdqZ41BT>*#BtHGI%NZo3$w)k>pKsN9G) z0k&3z8Z+=%$><@|!By;&@_V^3<=fgh5&BoM)Jm1?ZYr`T)+cr)U&8AOVDXBuC_~OqY=f-TY@KA78`+aT;S^nvwlMR zaKQ51hk9->^g|xv?ZLPD2;tMtwwB(&@}l*y2FvepYr`RlkxBEEekwpxdA6P?xyBPI z$##O)_HY|{-v5+`SKj3E&f{W`CcB!*Zh-9bVNeI~g-HdL3fQ&YYI(sE?VRWw@a&CE zYa_lluU7q4cMLo^^da;3lJh7kYkiy+aD44Ld>>Odzj^*iDGkjOoH!;|xO7yEd?y0& zIhaY;X6uzu?FKWG=zwmDKItIoDUi)wrD4_Bi;}9k+4U{PUKQ=mfT@$joPw)ghHIZm z@VCmnOga?yK{E^85BUQ=sTk}Av*1VFNCJ*gHOOLEegDo6!xz=NOzFpM_2HLLIlDMrzakVElavw)tj7p*gN4Vh#;8QO038(`xv@o(d< z0V~=MAk2w|?ZU36yEZ?Mir;~YQqV99e* zt1@CFo%!Hy8%Df27Hu7*W%NCqCk=e=CprAeg+p&q!jGpWcF*uc*}y+Y3G)PB6*wK_ z)mV* zyxv;<_7U27=|*zs{~+zHqpDom|6yqmlv298yE{cv5ox4DDe2yHH_|0Yh)8#LO9@DK zOE*aWX6t#*Ip615?_clI#oBA_b>H`%Yi915xju2pWt?Uuib#4~(UdGhq8i_^UeQQk zKSm<5@h%{YeRL{%m5zQ&U#NCUJu2;>!U9hSi7p@6lnvreLzcO6scc3?y`sFJDmhh| zQt4^*@AZ_KwO?F@h?4->1S^Fd=5i(B`l-(`8+)rW>_>vyZo5K&{S_bB3sb}&C^b{s zT;@E==OmwTrnIUAU~(dF@#6A?Va9vq#?hgV%(v1L-C3E%LeM?Az6CfjS`SLeJdF0n zb=27MSi(yM7#J!#8bz|KnN9GSw+|)UbHpp1jw^yfH#Fqr=}n>HK%E}_M`fdc9Ku`b z2A0gUUU4$E!*;E%%+NEH!YFYE_GpUR-X$bz8N{8aQaAP~sb4XyY7Yz1(56ba4MIAy zG`R8nTflVvH0V6F8)UB`j~8uN*V%-QO6>TlF{h2uuL1yC{UPvkXQl8qkNe3d&?evv z_p)`+Ql|NkEs*`>_}DTfcEM7`Aupt}N=KUoYk}_P?E4 zyg*Dt)?>m(!=Y77Swo!tfggs>VuB`g+ZykHk`si0vxDlVy`nFBma3xw9LCEZ> zF@zLHqFJ!?D)8dX6^2RPB34lI!NL1P1FZY3Gr>-*w7%G8?@3s-Wv~P2L-BI@X!0RQ zEKec9?>}AuI?bpUv7PJJzRiQ|h0$|SKO&CFCF-KeiVe>q&gP%%Nw8fXs4ptXzAk9Z zax{R0DM{sfr;9O09~ZuC1@DiSuxV!sq_Pm);59G2RNuSenOoj#c4V(g!4H$ui~(ud7KNP9w&JAd0s_gOvEy=Vz{Euuh>4nlB34 zf^Fi-x&-noH7Qsfx&qwDPFj-7Ww&jl@0K3E)B!%E19gF7)0p(70kjF^QX-R%C{CZ( z-JmolANF=KRvh%&Ww+dEy?`IW%s53kwOzcx;0YFly}*uTxD$YIo&GrcGjW_Hgojc)&U3tR0UV?I!9rJ}ILh!` zJt%Q-;!3lJ5dX8J^Ht6>a*|!!3|f{waiLD`#f&)%|K5XVkw>==WC}|vu?o61Tm74kmlOUS0%V=_rLyA~5GHOKDLl)jjHij;M z|A6!7G69%Qf^?(_m{OQM_)Fx`u2E-HLNC!pmmZ4_xqn+nQY!7-e%`7WBZU3vM?{;@ zx*;2i_JX@XzdNFNPRM#>xf@B@@e%>vOD8GHz1FhcR@VuGglji5da;bgx7a zS6zvtuWvB0HLME|eHnyuUXg~YK3gnl?kt9eEwp{FMIfmf$irVO+YH4m{%p;Ur}O z3i80=V12Mm>i5YILdNJ9g^G>fjNWTYquQv7d$iG_Qkx*OEO)p_-k2vkhMUy5EVAxN z%|_mtfv!MuIR9ktt1)cFe$V*Fkfnku7BEvl0_GTFi0vJBxBx@~uPp$VC8{239C zeuNQ$ET&NVc5Qpma)kMfaoh>wy>bA60HR9%X# zcwK>jqfZP%aV@JHC+P~!MTu2oKevQW+KYRAFG1NV79^hT91}JWEKaoJhPTER+)?z^ zoQ!L#()Y|cd5|icu4U*d z(!6WcovrYmB5@hkrC5_~hSBX@sX<4YJGFJ<4JIzH+T7YN9XxXED>4f#l;8(E$+BGE zUtqk1r(v<%>8K6Aa;F~=3YM95w+(CHgfZx7@^+zG!jcmu!&tm<)ltX}Ay~QeeBr?~ zu^=JiF%fw+oVIz_k=0vo0=t8RIrt;_q_iXDTT_r3T&zH$kMXgufXq%qj^uq4*ii4D zP3k${#v37EE_F0LpDsOj-Ql~STx7iCbTx=D8eEC-k!Qp)m*Q(%?Eeu_|B|;^uas`J z%qP$1{qWnx2p6&?-Kc&}3o3(NXAa9gzpBi#nLzhMJ0pB5t4>l2h1)BQD-w%>Bly;S za^1{gm-JTHk>}T~GJmZ?Z&7+zG%6e8weesO4eu}?2<;NeQ#1qzA|aAu(<-4AsTh9m zdvF0LvCh6>j>>fih=jE!?5mMs1lnVq54t~i$A2P&_0#c3FMb^tF-ZRv0jHbSZ_pIO z7|o%v^u&)!g!64jXpc8KubPvuNh~p90WvS*tkE6hRHo6c{lj2j={JsO6KrzCkhQKE zZqc_F?^j%$PReJGpSu4_iBt zZ=ar*!9tLWkztrPG>T-g0}Cy}(1NLTZfh?zNT1*9b9g&?og(?WSwr|Z2IoX54{Pra z@x+A42d!{MIUz=h`r7lemvnSdyzZdeE0RSUsh~sI{0Yw5t*NR;p52P3l#S z$#5d~wEB#{V|Q)RD^VU~L#z-xWi|iQ16-S>=3JpPS`nQugxIe=TFMtSn*OSv@N*o4cNCImM-LNx{E433NoTU8uC0xeTz{$ZY*YjBI%NOYPpcFl}OfZ6O(L=UKj* z)}k857=9SW`u(fyczB#}!|bA^D8s`IRd&Mdq3Zrv5&@ejwh^q^q~X6T+Q*JuU3dlc zo@LNpQZ`~VTavUr_$Y*7ZJE~gAP$BA=czyO!7U9o8s9WREH?!02Ob^AccWHmAtvOw zh(<%YkGxq)yoZ*zfEX5|elGk#MjO8__yz6E+3Q+0j;_VYspaxD#_$ zk(_p4ZAsIaEJsz|W|+y65UnE_LVx9@W-*1v@`W zM+6*bNMqCso5LP#5Un3BDi(9p-DC^XV>_at-*K93h;%sOTsYCxm+rHNJR>#;7$3fX z@rqIo8dDM+r0zpBJT%QSY`%DFK?3Q3N4p?2jBLi%MI$a59MgwUseEf_@aQJRdr7 zQNQ^1oM6}Z`A1~7W8-LFLSi^(M9t3Z0c4ULV!}latE|ANchEez1>r zM{C3rqCk5NXWQKx#nGP31zF-_G0*96?-ce^9VORi&JtQf();#6hL0>WpVbOh{=Kjv zhMZQZ)b~-*FO-uVXko~=enubhts;;qh zcA4O_J4rU^RB%d5cPSU<3T){#|#diF>wM z{NBSg>+H>3pR8Zs)9u(`)Z7;5C25jiKG?HmLCTwksOu1yJ3$0bHnUsS$s=MZ*IQ>& z6KcqIx3G8vXMU^M)M0wt8mvz3A|$ng^0*2y?WB32ny; zY;EHh))KnP%?R&%F}pR>h^hYp)+ea9=Qcsa?^<|2XAZq)!L6WT;xQguD~b?n@kv5z z9anOWq3?qdxCKh0qfC8YlnZUCY}`+lS^gNzy{%MFFA#=n*BUsqmIPC(&I&6A-lu2h zmKae)E5VOi2_-98s?-%~4AD7i5ZZ_DzwAl``|73Ufajw3`x zH`;MoEh%29@VDz8DSg2h)L;&T_HjG6&|G@W#&84vMu4=q zo@7fWY?KAr1^i0FaX^DDuER$;RvwrcKNJBWVH9Bl@^{0-&PV7HoN^i>{u<8^KiQc_$g7!e$nvB=I& zks|UJS}oR%(Mp||ZcYgo^{8!S@PGnGeL`0kz*8|JbDVV3Q;NTLe1LmC!DlKjFO#2F z!&dH2lS;mH*P+KyQbDkmUzq1IJ@I7^gMbLer0b+>yrdR zjre217nB69h}M-ary2}o`c z0l>U_AJ_4AlH1BggASA5 zNK7Ya%Z~;DGHUnQ7HH(usx7C9dVYP+l19~TaI*TzXFລO^U!xF@5kkk-2pC(h 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 r7^(aW11*;S+xdT*%Hs | 可选 | 自定义属性,用作用户或应用算法组件为数据对象添加扩展信息,参考 [DomainData 概念](../concepts/domaindata_cn.md) | -| partition | [Partition](#partition) | 可选 | 暂不支持 | -| columns | [DataColumn](#data-column)[] | 必填 | 列信息 | -| vendor | string | 可选 | 来源,用于批量查询接口筛选数据对象,参考 [ListDomainDataRequestData](#list-domain-data-request-data) 和 [DomainData 概念](../concepts/domaindata_cn.md) | -| author | string | 可选 | 表示 DomainData 的所属者的节点 ID ,用来标识这个 DomainData 是由哪个节点创建的 | +| 字段 | 类型 | 描述 | +|---------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------| +| domaindata_id | string | 数据对象 ID | +| name | string | 名称 | +| type | string | 类型,\[table,model,rule,report,unknown],大小写敏感 | +| relative_uri | string | 相对数据源所在位置的路径,参考 [DomainData 概念](../concepts/domaindata_cn.md) | +| domain_id | string | 节点 ID | +| datasource_id | string | 数据源 ID,参考 [DomainData 概念](../concepts/domaindata_cn.md) | +| attributes | map | 自定义属性,用作用户或应用算法组件为数据对象添加扩展信息,参考 [DomainData 概念](../concepts/domaindata_cn.md) | +| partition | [Partition](#partition) | 暂不支持 | +| columns | [DataColumn](#data-column)[] | 列信息 | +| vendor | string | 来源,用于批量查询接口筛选数据对象,参考 [ListDomainDataRequestData](#list-domain-data-request-data) 和 [DomainData 概念](../concepts/domaindata_cn.md) | +| author | string | 表示 DomainData 的所属者的节点 ID ,用来标识这个 DomainData 是由哪个节点创建的 | {#partition} diff --git a/docs/reference/apis/domaindatagrant_cn.md b/docs/reference/apis/domaindatagrant_cn.md index 1d54c063..05ee956c 100644 --- a/docs/reference/apis/domaindatagrant_cn.md +++ b/docs/reference/apis/domaindatagrant_cn.md @@ -31,7 +31,7 @@ DomainDataGrant 表示被 Kuscia 管理的数据授权对象。请参考 [Domain | 字段 | 类型 | 选填 | 描述 | |---------------|----------------------------------------------|----|--------------------------------------------------------------------------------------------------------------------------------| | header | [RequestHeader](summary_cn.md#requestheader) | 可选 | 自定义请求内容 | -| domaindatagrant_id | string | 可选 | 数据对象授权 ID,如果不填,则会由 kusciaapi 自动生成,并在 response 中返回。如果填写,则会使用填写的值,请注意需满足 [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) | +| domaindatagrant_id | string | 可选 | 数据对象授权 ID,如果不填,则会由 kusciaapi 自动生成,并在 response 中返回。如果填写,则会使用填写的值,请注意需满足 [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) | | domaindata_id | string | 必填 | 数据对象 ID | | grant_domain | string | 必填 | 被授权节点ID | | limit | [GrantLimit](#grant-limit-entity) | 选填 | 授权限制条件 | @@ -43,11 +43,11 @@ DomainDataGrant 表示被 Kuscia 管理的数据授权对象。请参考 [Domain #### 响应(CreateDomainDataGrantResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------------------|--------------------------------|----|---------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | CreateDomainDataGrantResponseData | 选填 | 授权信息结果 | -| data.domaindatagrant_id | string | 必填 | 数据对象授权 ID | +| 字段 | 类型 | 描述 | +|--------------------|--------------------------------|---------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | CreateDomainDataGrantResponseData | 授权信息结果 | +| data.domaindatagrant_id | string | 数据对象授权 ID | #### 请求示例 @@ -107,9 +107,9 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatagrant/create' \ #### 响应(UpdateDomainDataGrantResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -162,9 +162,9 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatagrant/update' \ #### 响应(DeleteDomainDataGrantResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -215,10 +215,10 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatagrant/delete' \ #### 响应(QueryDomainGrantResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | [DomainDataGrant](#domain-data-grant-entity) | 可选 | 授权信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | [DomainDataGrant](#domain-data-grant-entity) | 授权信息 | #### 请求示例 @@ -306,10 +306,10 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatagrant/query' \ #### 响应(BatchQueryDomainDataGrantResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|-------------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | [DomainDataGrant](#domain-data-grant-entity)[] | 可选 | 授权信息列表 | +| 字段 | 类型 | 描述 | +|--------|-------------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | [DomainDataGrant](#domain-data-grant-entity)[] | 授权信息列表 | #### 请求示例 @@ -402,36 +402,36 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatagrant/batchQuery' \ ### DomainDataGrant -| 字段 | 类型 | 选填 | 描述 | -|---------------|------------------------------|----|------------------------------------------------------------------------------------------------------------------------------------| -| data | [DomainDataGrantData](#domain-data-grant-data-entity) | 必填 | 数据对象ID | -| status | [DomainDataGrantStatus](#domain-data-grant-status-entity) | 必填 | 数据对象授权 ID | +| 字段 | 类型 | 描述 | +|---------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------| +| data | [DomainDataGrantData](#domain-data-grant-data-entity) | 数据对象ID | +| status | [DomainDataGrantStatus](#domain-data-grant-status-entity) | 数据对象授权 ID | {#domain-data-grant-data-entity} ### DomainDataGrantData -| 字段 | 类型 | 选填 | 描述 | -|---------------|------------------------------|----|------------------------------------------------------------------------------------------------------------------------------------| -| domaindatagrant_id | string | 必填 | 数据对象授权 ID | -| author | string | 必填 | 数据对象授权方节点 ID | -| domaindata_id | string | 必填 | 数据对象 ID | -| grant_domain | string | 必填 | 被授权节点 ID | -| limit | [GrantLimit](#grant-limit-entity) | 选填 | 授权限制条件 | -| description | map | 可选 | 自定义描述 | -| domain_id | string | 必填 | 授权信息所有者节点 ID | -| signature | string | 可选 | 表示授权信息的签名,是用 author 的节点私钥进行签名的。grantDomain 可以用 author 的公钥进行验证授权信息的真假。目前该字段为预留字段,暂未开启,暂时为空字符串 | +| 字段 | 类型 | 描述 | +|---------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------| +| domaindatagrant_id | string |数据对象授权 ID | +| author | string | 数据对象授权方节点 ID | +| domaindata_id | string | 数据对象 ID | +| grant_domain | string | 被授权节点 ID | +| limit | [GrantLimit](#grant-limit-entity) | 授权限制条件 | +| description | map | 自定义描述 | +| domain_id | string | 授权信息所有者节点 ID | +| signature | string | 表示授权信息的签名,是用 author 的节点私钥进行签名的。grantDomain 可以用 author 的公钥进行验证授权信息的真假。目前该字段为预留字段,暂未开启,暂时为空字符串 | {#domain-data-grant-status-entity} ### DomainDataGrantStatus -| 字段 | 类型 | 选填 | 描述 | -|---------------|------------------------------|----|------------------------------------------------------------------------------------------------------------------------------------| -| phase | string | 必填 | 状态 | -| message | string | 可选 | 状态描述信息 | -| records | UseRecord[] | 选填 | 授权使用记录 | -| records[].use_time | int64 | 选填 | 读取授权信息时间,Unix 时间戳,精确到纳秒 | -| records[].grant_domain | string | 选填 | 被授权节点 ID | -| records[].component | string | 选填 | 使用授权的组件 | -| records[].output | string | 选填 | 使用授权的任务输出 | +| 字段 | 类型 | 描述 | +|---------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------| +| phase | string | 状态 | +| message | string | 状态描述信息 | +| records | UseRecord[] | 授权使用记录 | +| records[].use_time | int64 | 读取授权信息时间,Unix 时间戳,精确到纳秒 | +| records[].grant_domain | string | 被授权节点 ID | +| records[].component | string | 使用授权的组件 | +| records[].output | string | 使用授权的任务输出 | diff --git a/docs/reference/apis/domaindatasource_cn.md b/docs/reference/apis/domaindatasource_cn.md index 1c0ac8fe..0a12e7a9 100644 --- a/docs/reference/apis/domaindatasource_cn.md +++ b/docs/reference/apis/domaindatasource_cn.md @@ -34,7 +34,7 @@ DomainDataSource 表示 Kuscia 管理的数据源。请参考 [DomainDataSource] |---------------|----------------------------------------------|----|--------------------------------------------------------------------------------------------------------------------------------| | header | [RequestHeader](summary_cn.md#requestheader) | 可选 | 自定义请求内容 | | domain_id | string | 必填 | 节点ID | -| datasource_id | string | 选填 | 数据源 ID,如果不填,则会由 kusciaapi 自动生成,并在 response 中返回。如果填写,则会使用填写的值,请注意需满足 [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) | +| datasource_id | string | 选填 | 数据源 ID,如果不填,则会由 kusciaapi 自动生成,并在 response 中返回。如果填写,则会使用填写的值,请注意需满足 [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) | | type | string | 必填 | 数据源类型,支持 localfs, oss, mysql | | name | string | 可选 | 数据源名称(无需唯一) | | info | [DataSourceInfo](#data-source-info) | 必填 | 数据源信息,详情见 [DataSourceInfo](#data-source-info) ,当设置 info_key 时,此字段可不填。 | @@ -45,10 +45,10 @@ DomainDataSource 表示 Kuscia 管理的数据源。请参考 [DomainDataSource] #### 响应(CreateDomainDataSourceResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------------------|--------------------------------|----|---------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data.datasource_id | string | 必填 | 数据源 ID | +| 字段 | 类型 | 描述 | +|--------------------|--------------------------------|---------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data.datasource_id | string | 数据源 ID | #### 请求示例 @@ -172,9 +172,9 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatasource/create' \ #### 响应(UpdateDomainDataSourceResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -233,9 +233,9 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatasource/update' \ #### 响应(DeleteDomainDataSourceResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -276,7 +276,7 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatasource/delete' \ /api/v1/domaindatasource/query -#### 请求(QueryDomainGrantRequest) +#### 请求(QueryDomainDataSourceRequest) | 字段 | 类型 | 选填 | 描述 | |--------|---------------------------------------------------------------|-----|--------------| @@ -284,12 +284,12 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatasource/delete' \ | domain_id | string | 必填 | 节点 ID | | datasource_id | string | 必填 | 数据源 ID | -#### 响应(QueryDomainGrantResponse) +#### 响应(QueryDomainDataSourceResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | [DomainDataSource](#domain-data-source-entity) | 可选 | 数据源信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | [DomainDataSource](#domain-data-source-entity) | 数据源信息 | #### 请求示例 @@ -369,10 +369,10 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatasource/query' \ #### 响应(BatchQueryDomainDataSourceResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|-------------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data.datasource_list | [DomainDataSource](#domain-data-source-entity)[] | 可选 | 数据源信息列表 | +| 字段 | 类型 | 描述 | +|--------|-------------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data.datasource_list | [DomainDataSource](#domain-data-source-entity)[] | 数据源信息列表 | #### 请求示例 @@ -492,10 +492,10 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatasource/batchQuery' \ #### 响应(ListDomainDataSourceResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|-------------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data.datasource_list | [DomainDataSource](#domain-data-source-entity)[] | 可选 | 数据源信息列表 | +| 字段 | 类型 | 描述 | +|--------|-------------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data.datasource_list | [DomainDataSource](#domain-data-source-entity)[] | 数据源信息列表 | #### 请求示例 @@ -645,13 +645,13 @@ curl -k -X POST 'https://localhost:8082/api/v1/domaindatasource/list' \ ### DomainDataSource -| 字段 | 类型 | 选填 | 描述 | -|---------------|------------------------------|----|------------------------------------------------------------------------------------------------------------------------------------| -| domain_id | string | 必填 | 节点 ID | -| datasource_id | string | 必填 | 数据源唯一标识 | -| name | string | 选填 | 数据源名称 | -| type | string | 必填 | 数据源类型,支持 localfs, oss, mysql | -| status | string | 必填 | 数据源的状态,暂未支持校验数据源的状态,现为空字符串 | -| info | [DataSourceInfo](#data-source-info) | 必填 | 数据源信息,详情见 [DataSourceInfo](#data-source-info) ,当设置 info_key 时,此字段可不填。 | -| info_key | string | 选填 | info 与 info_key 字段二者填一个即可,info_key 用于从 Kuscia ConfigManager 的加密后端中获取数据源的信息。 | -| access_directly | bool | 可选 | 隐私计算应用(如 SecretFlow )是否可直连访问数据源的标志位,true:应用直连访问数据源(不经过 DataProxy), false: 应用可通过 DataProxy 访问数据源(DataProxy暂未支持)。当前建设设置为 true 。| \ No newline at end of file +| 字段 | 类型 | 描述 | +|---------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------| +| domain_id | string | 节点 ID | +| datasource_id | string | 数据源唯一标识 | +| name | string | 数据源名称 | +| type | string | 数据源类型,支持 localfs, oss, mysql | +| status | string | 据源的状态,暂未支持校验数据源的状态,现为空字符串 | +| info | [DataSourceInfo](#data-source-info) | 数据源信息,详情见 [DataSourceInfo](#data-source-info) ,当设置 info_key 时,此字段可不填。 | +| info_key | string | info 与 info_key 字段二者填一个即可,info_key 用于从 Kuscia ConfigManager 的加密后端中获取数据源的信息。 | +| access_directly | bool | 隐私计算应用(如 SecretFlow )是否可直连访问数据源的标志位,true:应用直连访问数据源(不经过 DataProxy), false: 应用可通过 DataProxy 访问数据源(DataProxy暂未支持)。当前建设设置为 true 。| \ No newline at end of file diff --git a/docs/reference/apis/domainroute_cn.md b/docs/reference/apis/domainroute_cn.md index 62442a40..e864a1ff 100644 --- a/docs/reference/apis/domainroute_cn.md +++ b/docs/reference/apis/domainroute_cn.md @@ -28,23 +28,23 @@ protobuf 文件。 | 字段 | 类型 | 选填 | 描述 | |---------------------|----------------------------------------------|-----|--------------------------------------------------------------------------------------------| -| header | [RequestHeader](summary_cn.md#requestheader) | 可选 | 自定义请求内容 | +| header | [RequestHeader](summary_cn.md#requestheader) | 可选 | 请求头 | | authentication_type | string | 必填 | 认证类型:\[Token,MTLS,None],参考 [DomainRoute 概念](../concepts/domainroute_cn.md) | -| destination | string | 必填 | 目标节点 | -| endpoint | [RouteEndpoint](#route-endpoint) | 可选 | 目标节点的地址,如果没有配置 transit 则必填 | -| source | string | 必填 | 源节点 | -| token_config | [TokenConfig](#token-config) | 可选 | Token 相关配置,authenticationType 为`Token`或 bodyEncryption 非空时,源节点需配置 TokenConfig。该配置项在目标节点不生效 | -| mtls_config | [MtlsConfig](#mtls-config) | 可选 | MTLS 相关配置,authenticationType 为`MTLS`时,源节点需配置 mTLSConfig。该配置项在目标节点不生效 | +| destination | string | 必填 | 目标节点ID | +| endpoint | [RouteEndpoint](#route-endpoint) | 可选 | 目标节点的地址(请填写域名,端口和协议不需要填写);如果是[路由转发模式](../concepts/domainroute_cn.md#domain-route-advance)则不需要填该字段 | +| source | string | 必填 | 源节点ID | +| token_config | [TokenConfig](#token-config) | 可选 | Token 相关配置;authenticationType 为`Token`,需要配置该字段。 | +| mtls_config | [MtlsConfig](#mtls-config) | 可选 | MTLS 相关配置,authenticationType 为`MTLS`时,需要配置该字段。 | | transit | [Transit](#transit) | 可选 | 路由转发配置 | -| body_encryption | [BodyEncryption](#bodyencryption) | 可选 | 加密配置,一般在经第三方转发时使用,提升安全性 | +| body_encryption | [BodyEncryption](#bodyencryption) | 可选 | 加密配置;当路由为转发模式时需要配置该字段 | #### 响应(CreateDomainRouteResponse) -| 字段 | 类型 | 选填 | 描述 | -|-----------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | CreateDomainRouteResponseData | 必填 | | -| data.name | string | 必填 | 名称 | +| 字段 | 类型 | 描述 | +|-----------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | CreateDomainRouteResponseData | | +| data.name | string | 名称 | #### 请求示例 @@ -124,9 +124,9 @@ curl -k -X POST 'https://localhost:8082/api/v1/route/create' \ #### 响应(DeleteDomainResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -189,20 +189,20 @@ curl -k -X POST 'https://localhost:8082/api/v1/route/delete' \ #### 响应(QueryDomainRouteResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------------------------|----------------------------------|-----|----------------------------------------------------------------------------| -| status | [Status](./summary_cn.md#status) | 必填 | 状态信息 | -| data | QueryDomainRouteResponseData | 必填 | | -| data.name | string | 必填 | 节点名称 | -| data.authentication_type | string | 必填 | 认证类型:\[Token,MTLS,None],参考 [DomainRoute 概念](../concepts/domainroute_cn.md) | -| data.destination | string | 必填 | 目标节点 | -| data.endpoint | [RouteEndpoint](#route-endpoint) | 可选 | 目标节点的地址,参考 [DomainRoute 概念](../concepts/domainroute_cn.md) | -| data.source | string | 必填 | 源节点 | -| data.token_config | [TokenConfig](#token-config) | 可选 | Token 配置 | -| data.mtls_config | [MTLSConfig](#mtls-config) | 可选 | MTLS 配置 | -| data.transit | [Transit](#transit) | 可选 | 路由转发配置 | -| data.body_encryption | [BodyEncryption](#bodyencryption) | 可选 | 加密配置 | -| data.status | [RouteStatus](#route-status) | 必填 | 状态 | +| 字段 | 类型 | 描述 | +|--------------------------|----------------------------------|----------------------------------------------------------------------------| +| status | [Status](./summary_cn.md#status) | 状态信息 | +| data | QueryDomainRouteResponseData | | +| data.name | string | 节点名称 | +| data.authentication_type | string | 认证类型:\[Token,MTLS,None],参考 [DomainRoute 概念](../concepts/domainroute_cn.md) | +| data.destination | string | 目标节点 | +| data.endpoint | [RouteEndpoint](#route-endpoint) | 目标节点的地址,参考 [DomainRoute 概念](../concepts/domainroute_cn.md) | +| data.source | string | 源节点 | +| data.token_config | [TokenConfig](#token-config) | Token 配置 | +| data.mtls_config | [MTLSConfig](#mtls-config) | MTLS 配置 | +| data.transit | [Transit](#transit) | 路由转发配置 | +| data.body_encryption | [BodyEncryption](#bodyencryption) | 加密配置 | +| data.status | [RouteStatus](#route-status) | 状态 | #### 请求示例 @@ -292,11 +292,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/route/query' \ #### 响应(BatchQueryDomainRouteStatusResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|---------------------------------------------|----|--------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | BatchQueryDomainRouteStatusResponseData | | | -| data.routes | [DomainRouteStatus](#domain-route-status)[] | 必填 | 授权路由列表 | +| 字段 | 类型 | 描述 | +|-------------|---------------------------------------------|--------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | BatchQueryDomainRouteStatusResponseData | | +| data.routes | [DomainRouteStatus](#domain-route-status)[] | 授权路由列表 | #### 请求示例 @@ -387,12 +387,12 @@ curl -k -X POST 'https://localhost:8082/api/v1/route/status/batchQuery' \ ### DomainRouteStatus -| 字段 | 类型 | 选填 | 描述 | -|-------------|------------------------------|----|---------| -| name | string | 必填 | 名称 | -| destination | string | 必填 | 目标节点 ID | -| source | string | 必填 | 源节点 ID | -| status | [RouteStatus](#route-status) | 必填 | 状态 | +| 字段 | 类型 | 描述 | +|-------------|------------------------------|---------| +| name | string | 名称 | +| destination | string | 目标节点 ID | +| source | string | 源节点 ID | +| status | [RouteStatus](#route-status) | 状态 | {#endpoint-port} @@ -418,10 +418,10 @@ curl -k -X POST 'https://localhost:8082/api/v1/route/status/batchQuery' \ ### RouteStatus -| 字段 | 类型 | 选填 | 描述 | -|--------|--------|----|--------------------------| -| status | string | 必填 | 是否成功:\[Succeeded,Failed] | -| reason | string | 可选 | 原因 | +| 字段 | 类型 | 描述 | +|--------|--------|--------------------------| +| status | string | 是否成功:\[Succeeded,Failed] | +| reason | string | 原因 | {#mtls-config} diff --git a/docs/reference/apis/health_cn.md b/docs/reference/apis/health_cn.md index 6812c4d7..73993bb3 100644 --- a/docs/reference/apis/health_cn.md +++ b/docs/reference/apis/health_cn.md @@ -26,11 +26,11 @@ Health 提供了服务的健康检查,你可以借助这些 API 了解服务 #### 响应(HealthResponse) -| 字段 | 类型 | 选填 | 描述 | -|------------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | HealthResponseData | | | -| data.ready | bool | 必填 | 是否就绪 | +| 字段 | 类型 | 描述 | +|------------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | HealthResponseData | | +| data.ready | bool | 是否就绪 | #### 请求示例 diff --git a/docs/reference/apis/kusciajob_cn.md b/docs/reference/apis/kusciajob_cn.md index 3d517efb..440e46a0 100644 --- a/docs/reference/apis/kusciajob_cn.md +++ b/docs/reference/apis/kusciajob_cn.md @@ -34,7 +34,7 @@ protobuf 文件。 | 字段 | 类型 | 选填 | 描述 | |-----------------|----------------------------------------------|----|----------------------------------------------------------------------------------------------------------------------------| | header | [RequestHeader](summary_cn.md#requestheader) | 可选 | 自定义请求内容 | -| job_id | string | 必填 | JobID,满足 [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) | +| job_id | string | 必填 | JobID,满足 [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) | | initiator | string | 必填 | 发起方节点 ID | | max_parallelism | int32 | 可选 | 并发度,参考 [KusciaJob 概念](../concepts/kusciajob_cn.md) | | tasks | [Task](#task)[] | 必填 | 任务参数 | @@ -42,11 +42,11 @@ protobuf 文件。 #### 响应(CreateJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | CreateJobResponseData | | | -| data.job_id | string | 必填 | JobID | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | CreateJobResponseData | | +| data.job_id | string | JobID | #### 请求示例 @@ -146,15 +146,15 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/create' \ #### 响应(QueryJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|----------------------|---------------------------------------|----|--------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | QueryJobResponseData | | | -| data.job_id | string | 必填 | JobID | -| data.initiator | string | 必填 | 发起方 | -| data.max_parallelism | int32 | 必填 | 并发度 | -| data.tasks | [TaskConfig](#task-config)[] | 必填 | 任务列表 | -| data.status | [JobStatusDetail](#job-status-detail) | 必填 | Job 状态 | +| 字段 | 类型 | 描述 | +|----------------------|---------------------------------------|--------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | QueryJobResponseData | | +| data.job_id | string | JobID | +| data.initiator | string | 发起方 | +| data.max_parallelism | int32 | 并发度 | +| data.tasks | [TaskConfig](#task-config)[] | 任务列表 | +| data.status | [JobStatusDetail](#job-status-detail) | Job 状态 | | data.custom_fields | map | 可选 | 自定义参数 | #### 请求示例 @@ -278,11 +278,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/query' \ #### 响应(BatchQueryJobStatusResponse) -| 字段 | 类型 | 选填 | 描述 | -|-----------|---------------------------------|----|----------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | BatchQueryJobStatusResponseData | | | -| data.jobs | [JobStatus](#job-status)[] | 必填 | Job 状态列表 | +| 字段 | 类型 | 描述 | +|-----------|---------------------------------|----------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | BatchQueryJobStatusResponseData | | +| data.jobs | [JobStatus](#job-status)[] | Job 状态列表 | #### 请求示例 @@ -367,11 +367,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/status/batchQuery' \ #### 响应(DeleteJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | DeleteJobResponseData | | | -| data.job_id | string | 必填 | JobID | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | DeleteJobResponseData | | +| data.job_id | string | JobID | #### 请求示例 @@ -424,11 +424,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/delete' \ #### 响应(StopJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | StopJobResponseData | | | -| data.job_id | string | 必填 | JobID | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | StopJobResponseData | | +| data.job_id | string | JobID | #### 请求示例 @@ -480,10 +480,10 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/stop' \ #### 响应(WatchJobEventResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------|----|--------| -| 类型 | [EventType](#event-type) | 必填 | 事件类型 | -| object | [JobStatus](#job-status) | 必填 | Job 状态 | +| 字段 | 类型 | 描述 | +|--------|--------------------------|--------| +| 类型 | [EventType](#event-type) | 事件类型 | +| object | [JobStatus](#job-status) | Job 状态 | {#approval-job} @@ -500,17 +500,17 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/stop' \ | 字段 | 类型 | 选填 | 描述 | |-----------------|----------------------------------------------|----|----------------------------------------------------------------------------------------------------------------------------| | header | [RequestHeader](summary_cn.md#requestheader) | 可选 | 自定义请求内容 | -| job_id | string | 必填 | JobID,满足 [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) | +| job_id | string | 必填 | JobID,满足 [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) | | result | [ApproveResult](approve-result) | 必填 | 审批结果,接收或拒绝,接收则作业(job)可以执行,拒绝则作业不可执行 | | | reason | string | 可选 | 接收或拒绝的理由 | #### 响应(ApprovalJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | ApprovalJobResponseData | | | -| data.job_id | string | 必填 | JobID | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | ApprovalJobResponseData | | +| data.job_id | string | JobID | {#suspend-job} @@ -531,11 +531,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/stop' \ #### 响应(SuspendJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | SuspendJobResponseData | | | -| data.job_id | string | 必填 | JobID | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | SuspendJobResponseData | | +| data.job_id | string | JobID | #### 请求示例 @@ -589,11 +589,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/suspend' \ #### 响应(RestartJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | RestartJobResponseData | | | -| data.job_id | string | 必填 | JobID | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | RestartJobResponseData | | +| data.job_id | string | JobID | #### 请求示例 @@ -646,11 +646,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/restart' \ #### 响应(CancelJobResponse) -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | CancelJobResponseData | | | -| data.job_id | string | 必填 | JobID | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | CancelJobResponseData | | +| data.job_id | string | JobID | #### 请求示例 @@ -691,23 +691,23 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/cancel' \ ### JobStatus -| 字段 | 类型 | 选填 | 描述 | -|--------|---------------------------------------|----|----------| -| job_id | string | 必填 | JobID | -| status | [JobStatusDetail](#job-status-detail) | 必填 | Job 状态详情 | +| 字段 | 类型 | 描述 | +|--------|---------------------------------------|----------| +| job_id | string | JobID | +| status | [JobStatusDetail](#job-status-detail) | Job 状态详情 | {#job-status-detail} ### JobStatusDetail -| 字段 | 类型 | 选填 | 描述 | -|-------------|------------------------------|----|--------------------------| -| state | string | 必填 | 作业状态, 参考 [State](#state) | -| err_msg | string | 可选 | 错误信息 | -| create_time | string | 必填 | 创建时间 | -| start_time | string | 必填 | 启动时间 | -| end_time | string | 可选 | 结束时间 | -| tasks | [TaskStatus](#task-status)[] | 必填 | 任务列表 | +| 字段 | 类型 | 描述 | +|-------------|------------------------------|--------------------------| +| state | string | 作业状态, 参考 [State](#state) | +| err_msg | string | 错误信息 | +| create_time | string | 创建时间 | +| start_time | string | 启动时间 | +| end_time | string | 结束时间 | +| tasks | [TaskStatus](#task-status)[] | 任务列表 | {#party} @@ -732,12 +732,12 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/cancel' \ ### PartyStatus -| 字段 | 类型 | 选填 | 描述 | -|-----------|-------------------------------------------|----|-----------------------------| -| domain_id | string | 必填 | 节点 ID | -| state | string | 必填 | 参与方任务状态, 参考 [State](#state) | -| err_msg | string | 可选 | 错误信息 | -| endpoints | [JobPartyEndpoint](#job-party-endpoint)[] | 必填 | 应用对外暴露的访问地址信息 | +| 字段 | 类型 | 描述 | +|-----------|-------------------------------------------|-----------------------------| +| domain_id | string | 节点 ID | +| state | string | 参与方任务状态, 参考 [State](#state) | +| err_msg | string | 错误信息 | +| endpoints | [JobPartyEndpoint](#job-party-endpoint)[] | 应用对外暴露的访问地址信息 | {#task} @@ -747,8 +747,8 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/cancel' \ |-------------------|-------------------|----|----------------------------------------------------------------------------------------------------------------------------------------------------------| | app_image | string | 必填 | 任务镜像 | | parties | [Party](#party)[] | 必填 | 参与方节点 ID | -| alias | string | 必填 | 任务别名,同一个 Job 中唯一,满足 [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) | -| task_id | string | 可选 | 任务 ID,如果不填,Kuscia 将随机生成唯一的 task_id ,满足 [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) | +| alias | string | 必填 | 任务别名,同一个 Job 中唯一,满足 [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) | +| task_id | string | 可选 | 任务 ID,如果不填,Kuscia 将随机生成唯一的 task_id ,满足 [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) | | dependencies | string[] | 必填 | 依赖任务,通过 alias 字段来编排 Job 中 Task 之间的依赖关系 | | task_input_config | string | 必填 | 任务配置 | | priority | string | 可选 | 优先级,值越大优先级越高 | @@ -757,30 +757,30 @@ curl -k -X POST 'https://localhost:8082/api/v1/job/cancel' \ ### TaskConfig -| 字段 | 类型 | 选填 | 描述 | -|-------------------|-------------------|----|--------------| -| app_image | string | 必填 | 任务镜像 | -| parties | [Party](#party)[] | 必填 | 参与方 | -| alias | string | 必填 | 任务别名 | -| task_id | string | 可选 | 任务 ID | -| dependencies | string[] | 必填 | 依赖任务 | -| task_input_config | string | 必填 | 任务配置 | -| priority | string | 可选 | 优先级,值越大优先级越高 | +| 字段 | 类型 | 描述 | +|-------------------|-------------------|--------------| +| app_image | string | 任务镜像 | +| parties | [Party](#party)[] | 参与方 | +| alias | string | 任务别名 | +| task_id | string | 任务 ID | +| dependencies | string[] | 依赖任务 | +| task_input_config | string | 任务配置 | +| priority | string | 优先级,值越大优先级越高 | {#task-status} ### TaskStatus -| 字段 | 类型 | 选填 | 描述 | -|-------------|--------------------------------|----|-------------------------| -| task_id | string | 可选 | 任务 ID | -| alias | string | 必填 | 任务别名 | -| state | string | 必填 | 任务状态,参考 [State](#state) | -| err_msg | string | 可选 | 错误信息 | -| create_time | string | 必填 | 创建事件 | -| start_time | string | 必填 | 开始事件 | -| end_time | string | 可选 | 结束事件 | -| parties | [PartyStatus](#party-status)[] | 必填 | 参与方 | +| 字段 | 类型 | 描述 | +|-------------|--------------------------------|-------------------------| +| task_id | string | 任务 ID | +| alias | string | 任务别名 | +| state | string | 任务状态,参考 [State](#state) | +| err_msg | string | 错误信息 | +| create_time | string | 创建事件 | +| start_time | string | 开始事件 | +| end_time | string | 结束事件 | +| parties | [PartyStatus](#party-status)[] | 参与方 | {#event-type} @@ -826,8 +826,8 @@ KusciaJob 状态详细介绍见[文档](../concepts/kusciajob_cn.md#kuscia-state ### JobPartyEndpoint -| 字段 | 类型 | 选填 | 描述 | -|-----------|--------|---|-----------------------------------------------------------------------------------------------------| -| port_name | string | 必填 | 应用服务端口名称,详细解释请参考[AppImage](../concepts/appimage_cn.md) `deployTemplates.spec.containers.ports.name` | -| scope | string | 必填 | 应用服务使用范围,详细解释请参考[AppImage](../concepts/appimage_cn.md) `deployTemplates.spec.containers.ports.scope` | -| endpoint | string | 必填 | 应用服务访问地址 | +| 字段 | 类型 | 描述 | +|-----------|--------|-----------------------------------------------------------------------------------------------------| +| port_name | string | 应用服务端口名称,详细解释请参考[AppImage](../concepts/appimage_cn.md) `deployTemplates.spec.containers.ports.name` | +| scope | string | 应用服务使用范围,详细解释请参考[AppImage](../concepts/appimage_cn.md) `deployTemplates.spec.containers.ports.scope` | +| endpoint | string | 应用服务访问地址 | \ No newline at end of file diff --git a/docs/reference/apis/serving_cn.md b/docs/reference/apis/serving_cn.md index 2055d697..b747359e 100644 --- a/docs/reference/apis/serving_cn.md +++ b/docs/reference/apis/serving_cn.md @@ -28,7 +28,7 @@ | 字段 | 类型 | 选填 | 描述 | |----------------------|----------------------------------------------|----|-------------------------------------------------------------------------------------------------------------------------------| | header | [RequestHeader](summary_cn.md#requestheader) | 可选 | 自定义请求内容 | -| serving_id | string | 必填 | ServingID,满足[DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names) | +| serving_id | string | 必填 | ServingID,满足[RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names) | | serving_input_config | string | 必填 | 预测配置 | | initiator | string | 必填 | 发起方节点ID | | parties | [ServingParty](#serving-party)[] | 必填 | 参与方信息 | @@ -36,9 +36,9 @@ #### 响应(CreateServingResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -133,9 +133,9 @@ curl -k -X POST 'https://localhost:8082/api/v1/serving/create' \ #### 响应(UpdateServingResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -226,9 +226,9 @@ curl -k -X POST 'https://localhost:8082/api/v1/serving/update' \ #### 响应(DeleteServingResponse) -| 字段 | 类型 | 选填 | 描述 | -|--------|--------------------------------|----|------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | +| 字段 | 类型 | 描述 | +|--------|--------------------------------|------| +| status | [Status](summary_cn.md#status) | 状态信息 | #### 请求示例 @@ -278,14 +278,14 @@ curl -k -X POST 'https://localhost:8082/api/v1/serving/delete' \ #### 响应(QueryServingResponse) -| 字段 | 类型 | 选填 | 描述 | -|---------------------------|-----------------------------------------------|----|-----------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | QueryServingResponseData | | | -| data.serving_input_config | string | 必填 | 预测配置 | -| data.initiator | string | 必填 | 发起方节点ID | -| data.parties | [ServingParty](#serving-party)[] | 必填 | 参与方信息 | -| data.status | [ServingStatusDetail](#serving-status-detail) | 必填 | Serving状态 | +| 字段 | 类型 | 描述 | +|---------------------------|-----------------------------------------------|-----------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | QueryServingResponseData | | +| data.serving_input_config | string | 预测配置 | +| data.initiator | string | 发起方节点ID | +| data.parties | [ServingParty](#serving-party)[] | 参与方信息 | +| data.status | [ServingStatusDetail](#serving-status-detail) | Serving状态 | #### 请求示例 @@ -445,11 +445,11 @@ curl -k -X POST 'https://localhost:8082/api/v1/serving/query' \ #### 响应(BatchQueryServingStatusResponse) -| 字段 | 类型 | 选填 | 描述 | -|---------------|-------------------------------------|----|-------------| -| status | [Status](summary_cn.md#status) | 必填 | 状态信息 | -| data | BatchQueryServingStatusResponseData | | | -| data.servings | [ServingStatus](#serving-status)[] | 必填 | Serving状态列表 | +| 字段 | 类型 | 描述 | +|---------------|-------------------------------------|-------------| +| status | [Status](summary_cn.md#status) | 状态信息 | +| data | BatchQueryServingStatusResponseData | | +| data.servings | [ServingStatus](#serving-status)[] | Serving状态列表 | #### 请求示例 @@ -560,42 +560,42 @@ curl -k -X POST 'https://localhost:8082/api/v1/serving/status/batchQuery' \ ### ServingStatus -| 字段 | 类型 | 选填 | 描述 | -|------------|-----------------------------------------------|----|-------------| -| serving_id | string | 必填 | ServingID | -| status | [ServingStatusDetail](#serving-status-detail) | 必填 | Serving状态详情 | +| 字段 | 类型 | 描述 | +|------------|-----------------------------------------------|-------------| +| serving_id | string | ServingID | +| status | [ServingStatusDetail](#serving-status-detail) | Serving状态详情 | {#serving-status-detail} ### ServingStatusDetail -| 字段 | 类型 | 选填 | 描述 | -|-------------------|-----------------------------------------------|----|------------------------------| -| state | string | 必填 | Serving状态,参考 [State](#state) | -| reason | string | 可选 | Serving处于该状态的原因,一般用于描述失败的状态 | -| message | string | 可选 | Serving处于该状态的详细信息,一般用于描述失败的状态 | -| total_parties | int32 | 必填 | 参与方总数 | -| available_parties | int32 | 必填 | 可用参与方数量 | -| create_time | string | 必填 | 创建时间 | -| party_statuses | [PartyServingStatus](#party-serving-status)[] | 必填 | 参与方状态 | +| 字段 | 类型 | 描述 | +|-------------------|-----------------------------------------------|------------------------------| +| state | string | Serving状态,参考 [State](#state) | +| reason | string | Serving处于该状态的原因,一般用于描述失败的状态 | +| message | string | Serving处于该状态的详细信息,一般用于描述失败的状态 | +| total_parties | int32 | 参与方总数 | +| available_parties | int32 | 可用参与方数量 | +| create_time | string | 创建时间 | +| party_statuses | [PartyServingStatus](#party-serving-status)[] | 参与方状态 | {#party-serving-status} ### PartyServingStatus -| 字段 | 类型 | 选填 | 描述 | -|----------------------|---------------------------------------------------|----|------------------------| -| domain_id | string | 必填 | 节点ID | -| role | string | 可选 | 角色 | -| state | string | 必填 | 状态,参考 [State](#state) | -| replicas | int32 | 必填 | 应用副本总数 | -| available_replicas | int32 | 必填 | 应用可用副本数 | -| unavailable_replicas | int32 | 必填 | 应用不可用副本数 | -| updatedReplicas | int32 | 必填 | 最新版本的应用副本数 | -| create_time | string | 必填 | 创建时间 | -| endpoints | [ServingPartyEndpoint](#serving-party-endpoint)[] | 必填 | 应用对外暴露的访问地址信息 | +| 字段 | 类型 | 描述 | +|----------------------|---------------------------------------------------|------------------------| +| domain_id | string | 节点ID | +| role | string | 角色 | +| state | string | 状态,参考 [State](#state) | +| replicas | int32 | 应用副本总数 | +| available_replicas | int32 | 应用可用副本数 | +| unavailable_replicas | int32 | 应用不可用副本数 | +| updatedReplicas | int32 | 最新版本的应用副本数 | +| create_time | string | 创建时间 | +| endpoints | [ServingPartyEndpoint](#serving-party-endpoint)[] | 应用对外暴露的访问地址信息 | {#serving-party-endpoint} diff --git a/docs/reference/concepts/domain_cn.md b/docs/reference/concepts/domain_cn.md index 0313675b..4eccc7a6 100644 --- a/docs/reference/concepts/domain_cn.md +++ b/docs/reference/concepts/domain_cn.md @@ -34,7 +34,7 @@ spec: - `.spec.role`:表示隐私计算节点 Domain 的角色,默认为 `""`。支持两种取值:`partner` 和 `""`。 - `partner`:表示外部节点,用在点对点组网模式下的协作方节点。点对点组网模式下,需要在任务调度方的集群中创建协作方的 Domain,在创建该 Domain 时,需要将 `role` 的值设置为 `partner` 。 - `""`:表示内部节点。 -- `.spec.cert`:表示 BASE64 编码格式的隐私计算节点证书。 +- `.spec.cert`:表示 BASE64 编码格式的隐私计算节点证书,该证书是配置文件中的 `domainKeyData` 私钥产生的,可以通过[这里](https://github.com/secretflow/kuscia/blob/main/scripts/deploy/generate_cert.sh)的命令生成。在中心化模式场景不需要填充该字段。 - `.spec.resourceQuota.podMaxCount`:表示 Domain 所管理的隐私计算节点命名空间(Namespace)下所允许创建的最大 Pod 数量,当前示例为`100`。 @@ -169,7 +169,7 @@ Domain `spec` 的子字段详细介绍如下: - `role`:表示隐私计算节点 Domain 的角色,默认为`""`。支持两种取值:`partner `和 `""` 。 - `partner`:表示外部节点,用在点对点组网模式下的协作方节点。 点对点组网模式下,需要在任务调度方的集群中创建协作方的 Domain,在创建该 Domain 时,需要将 `role` 的值设置为 `partner` 。 - `""`:表示内部节点。 -- `cert`:表示 BASE64 编码格式的隐私计算节点证书。用于节点之间 Token 协商或 TLS 认证。 +- `cert`:表示 BASE64 编码格式的隐私计算节点证书,该证书是配置文件中的 `domainKeyData` 私钥产生的,可以通过[这里](https://github.com/secretflow/kuscia/blob/main/scripts/deploy/generate_cert.sh)的命令生成。在中心化模式场景不需要填充该字段。 - `interConnProtocols`:表示外部隐私计算节点支持的互联互通作业协议类型,默认为 `""`。支持两种取值:`kuscia` 和 `bfia` 。当前该字段只支持配置一种协议,若配置多个协议,则会选择第一个协议作为互联互通作业的协议类型。未来会支持多种协议。 - `kuscia`:表示该外部节点参与隐私计算任务时,会使用互联互通蚂蚁 `kuscia` 协议运行隐私计算任务。 - `bfia`:表示该外部节点参与隐私计算任务时,会使用互联互通银联 `bfia` 协议运行隐私计算任务。 diff --git a/docs/reference/concepts/domainroute_cn.md b/docs/reference/concepts/domainroute_cn.md index 172847bc..9ea6d82f 100644 --- a/docs/reference/concepts/domainroute_cn.md +++ b/docs/reference/concepts/domainroute_cn.md @@ -396,6 +396,8 @@ ClusterDomainRoute `status` 的子字段详细介绍如下: * `destinationTokens[].revisionTime`:表示 Token 时间戳。 * `destinationTokens[].token`:表示 BASE64 编码格式的经过节点公钥加密的 Token。 +{#domain-route-advance} + ## DomainRoute 进阶 进阶部分介绍 DomainRoute 的转发能力。在 Kuscia 架构中,路由转发能力是通过配置 ClusterDomainRoute(CDR)中的 `Transit` 字段来实现的。 diff --git a/docs/reference/concepts/kusciajob_cn.md b/docs/reference/concepts/kusciajob_cn.md index 415dc6ca..b92fcef9 100644 --- a/docs/reference/concepts/kusciajob_cn.md +++ b/docs/reference/concepts/kusciajob_cn.md @@ -95,7 +95,7 @@ spec: - 有两个 KusciaTask 将会被依次创建,分别是用于隐私求交的 job-psi 和用于随机分割的 job-split。 KusciaJob Controller 将会先创建 job-psi, 当 job-psi 任务成功后,job-split 才会被创建,这个依赖关系由 job-split 的`dependencies`指定。**创建的 KusciaTask 的`.metadata.name`将会由这里的 `taskID`指定。若你自行指定该 `taskID`,那么你应当保证当前系统中没有同名的 KusciaTask ,同时还需要保证所有的`taskID`满足 - [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names)**。 + [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names)**。 若你不指定 KusciaJob 中的 taskID,则 KusciaJob Controller 会生成该 taskID。 - `parties`指定了任务参与方的 ID 和角色,这个字段说明了这个算子将会在哪些参与方以什么角色执行。 - `appImage`用于表明 KusciaTask 在多方计算中执行的算法镜像。你可以通过查看 [AppImage](./appimage_cn.md) 获得更多的信息。 @@ -365,7 +365,7 @@ KusciaJob `spec`的子字段详细介绍如下: - `maxParallelism`:表示可以同时处于 Running 状态的任务的最大数量,可选,默认为 1,范围为 1-128。 - `tasks`:表示要执行的任务列表,最多 128 个。 - `alias`:表示任务的别名,必填。KusciaJob 中所有任务的别名不能重复。 - - `tasks[].taskID`:用作任务依赖标识,全局唯一,满足 [DNS 子域名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names)。 + - `tasks[].taskID`:用作任务依赖标识,全局唯一,满足 [RFC 1123 标签名规则要求](https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/names/#dns-label-names)。 若任务发起方为 Kuscia 中的节点,且未指定该标识,则 KusciaJob Controller 会生成全局唯一的任务标识。 - `tasks[].priority`:表示任务优先级,根据 maxParallelism,当存在多个 KusciaTask 可以被创建时,该值较高的优先被创建。 - `tasks[].tolerable`:表示是否可以容忍任务失败,详见 [任务分类](#task-classification)。 diff --git a/docs/reference/troubleshoot/custom_registry.md b/docs/reference/troubleshoot/custom_registry.md new file mode 100644 index 00000000..da6b5269 --- /dev/null +++ b/docs/reference/troubleshoot/custom_registry.md @@ -0,0 +1,57 @@ +# 使用自定义镜像仓库 + + +Kuscia支持自动拉取远程的应用镜像(比如:secretflow等),这样可以不用手动导入镜像到容器中。可以在[Kuscia配置文件](../../deployment/kuscia_config_cn.md)中配置私有(or公开)镜像仓库地址。 + +## 如何配置使用自定义镜像仓库 +配置文件中的 `image` 字段用来配置自定义仓库。相关含义参考 [Kuscia配置文件说明](../../deployment/kuscia_config_cn.md) + +### 私有镜像仓库 +如果有一个私有镜像仓库 (示例:`private.registry.com`), 对应的配置如下: + +``` +- image: + - defaultRegistry: private #随意,只需要对应到即可 + - registries: + - name: private + endpoint: private.registry.com/test + username: testname + password: testpass +``` + +### 公开镜像仓库 +如果使用公开的镜像仓库 (示例:`secretflow-registry.cn-hangzhou.cr.aliyuncs.com`),对应的配置如下: + +``` +- image: + - defaultRegistry: aliyun #随意,只需要对应到即可 + - registries: + - name: aliyun + endpoint: secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow +``` + + +## 关于镜像仓库和AppImage的搭配使用 + +配置文件中有`image`字段,`AppImage` 中也存在image相关的配置,他们的搭配关系示例如下: + +| 配置文件 | AppImage配置 | 实际镜像地址 | 备注 | +| - | - | - | - | +| 无配置 | secretflow/app:v1 | docker.io/secretflow/app:v1 | | +| 无配置 | private.registry.com/secretflow/app:v1 | private.registry.com/secretflow/app:v1 | | +| private.registry.com | secretflow/app:v1 | private.registry.com/app:v1 | | +| private.registry.com/secretflow | app:v1 | private.registry.com/secretflow/app:v1 | 推荐配置 | +| private.registry.com/secretflow | secretflow/app:v1 | private.registry.com/secretflow/app:v1 | | +| private.registry.com/secretflow | test/app:v1 | private.registry.com/secretflow/app:v1 | | +| private.registry.com/secretflow | private.registry.com/secretflow/app:v1 | private.registry.com/secretflow/app:v1 | | +| private.registry.com/secretflow | public.aliyun.com/secretflow/app:v1 | public.aliyun.com/secretflow/app:v1 | 强烈不推荐配置,未来可能会禁止这种配置 | + + +注:Kuscia推荐在`AppImage`中只配置镜像名(不带镜像仓库地址),否则切换仓库的时候,需要批量修改`AppImage`,所以不建议如此配置。 + +## 镜像拉取失败 +当发现镜像拉取失败时,请确认 配置文件中仓库地址,以及账密相关配置是否正确, 以及参考上文,确保AppImage的镜像地址配置正确. + +``` +2024-06-06 13:33:00.534 ERROR framework/pod_workers.go:978 Error syncing pod "ant-test-0_ant(7fd5285b-2a5c-4a75-930a-2908e98c8799)", skipping: failed to "StartContainer" for "test" with ErrImagePull: "faile to pull image \"registry.xxxx.com/secretflow/nginx:v1\" with credentials, detail-> rpc error: code = Unknown desc = failed to pull and unpack image \"registry.xxxx.com/secretflow/nginx:v1\": failed to resolve reference \"registry.xxxx.com/secretflow/nginx:v1\": unexpected status from HEAD request to https://registry.xxxx.com/v2/secretflow/nginx/manifests/v1: 401 Unauthorized" +``` \ No newline at end of file diff --git a/docs/reference/troubleshoot/index.rst b/docs/reference/troubleshoot/index.rst index e432de56..26297eca 100644 --- a/docs/reference/troubleshoot/index.rst +++ b/docs/reference/troubleshoot/index.rst @@ -16,4 +16,6 @@ private_key_loss protocol_describe docker_cpp_copy - docker_memory_limit \ No newline at end of file + docker_memory_limit + k8s_ulimit_check + custom_registry \ No newline at end of file diff --git a/docs/reference/troubleshoot/k8s_ulimit_check.md b/docs/reference/troubleshoot/k8s_ulimit_check.md new file mode 100644 index 00000000..645c8dc8 --- /dev/null +++ b/docs/reference/troubleshoot/k8s_ulimit_check.md @@ -0,0 +1,18 @@ +# Kuscia K8s 部署模式下 SecretFlow 应用线程限制问题 + +## 问题描述 +在 Kuscia K8s 部署模式下,SecretFlow 应用运行过程中出现创建线程失败问题:`pthread_create failed: Resource temporarily unavailable`。 + +## 原因分析 + +K8s 集群 Kubelet 组件的 `podPidsLimit` 参数被限制为某个特定值,而 SecretFlow 默认启动线程数为:物理机 CPU * 32 + Ray 的线程数 ,如果 SecretFlow 运行启动的线程数超过了 Kubelet 限制的线程数时则会出现以上报错。 + +## 解决方案 + +1、找到 kubelet.service 的配置文件 /var/lib/kubelet/config.yaml(此处以官网推荐路径为例),修改 `podPidsLimit` 参数,例如 `podPidsLimit: -1`。详情参考 [K8s 官方文档](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)。 + +2、修改后重启 Kubelet 服务 +```shell +systemctl daemon-reload +systemctl restart kubelet.service +``` \ No newline at end of file diff --git a/docs/tutorial/run_bfia_job_cn.md b/docs/tutorial/run_bfia_job_cn.md index 94956d67..153f0e18 100644 --- a/docs/tutorial/run_bfia_job_cn.md +++ b/docs/tutorial/run_bfia_job_cn.md @@ -125,6 +125,7 @@ apiVersion: kuscia.secretflow/v1alpha1 kind: KusciaJob metadata: name: job-ss-lr + namespace: cross-domain spec: initiator: alice tasks: @@ -215,6 +216,7 @@ metadata: kuscia.secretflow/interconn-protocol-type: bfia kuscia.secretflow/self-cluster-as-initiator: "true" name: job-ss-lr + namespace: cross-domain resourceVersion: "50438" uid: 408a03ae-69c2-4fa8-a638-b47b6dbf530f spec: @@ -312,7 +314,7 @@ more /home/kuscia/var/storage/job-ss-lr-guest-0/job-ss-lr-{random-id}-result 当你想清理这个 KusciaJob 时,你可以通过下面的命令完成: ```shell -kubectl delete kj job-ss-lr +kubectl delete kj job-ss-lr -n cross-domain ``` 当这个 KusciaJob 被清理时, 这个 KusciaJob 创建的 KusciaTask 也会一起被清理。 diff --git a/go.mod b/go.mod index 8020b2b9..693c03c4 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/secretflow/kuscia -go 1.19 +go 1.22 require ( + github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/apache/arrow/go/v13 v13.0.0 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e + github.com/aws/aws-sdk-go v1.44.256 github.com/casbin/casbin/v2 v2.77.2 github.com/containerd/cgroups/v3 v3.0.3 github.com/coredns/caddy v1.1.1 @@ -19,7 +21,8 @@ require ( github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.6.0 - github.com/google/uuid v1.5.0 + github.com/google/uuid v1.6.0 + github.com/johannesboyne/gofakes3 v0.0.0-20240513200200-99de01ee122d github.com/json-iterator/go v1.1.12 github.com/miekg/dns v1.1.50 github.com/mitchellh/go-homedir v1.1.0 @@ -33,7 +36,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.17.0 github.com/rosedblabs/rosedb/v2 v2.3.2 - github.com/secretflow/kuscia-envoy v0.0.0-20240604074449-2649bf6e27cb + github.com/secretflow/kuscia-envoy v0.0.0-20240402083426-b0884d002f48 github.com/shirou/gopsutil/v3 v3.22.6 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 @@ -43,7 +46,7 @@ require ( gitlab.com/jonas.jasas/condchan v0.0.0-20190210165812-36637ad2b5bc go.uber.org/atomic v1.9.0 go.uber.org/zap v1.24.0 - golang.org/x/net v0.19.0 + golang.org/x/net v0.23.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 google.golang.org/grpc v1.60.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 @@ -89,7 +92,6 @@ require ( github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect - github.com/aws/aws-sdk-go v1.44.116 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect @@ -159,8 +161,8 @@ require ( github.com/jonboulle/clockwork v0.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/karrick/godirwalk v1.17.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lithammer/dedent v1.1.0 // indirect @@ -191,7 +193,9 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/rosedblabs/wal v1.3.3 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect + github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect @@ -224,13 +228,13 @@ require ( go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.16.0 // indirect diff --git a/go.sum b/go.sum index ba81bbdf..32a95496 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/agiledragon/gomonkey/v2 v2.11.0 h1:5oxSgA+tC1xuGsrIorR+sYiziYltmJyEZ9qA25b6l5U= +github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -66,9 +68,10 @@ github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/Y github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= -github.com/aws/aws-sdk-go v1.44.116 h1:NpLIhcvLWXJZAEwvPj3TDHeqp7DleK6ZUVYyW01WNHY= -github.com/aws/aws-sdk-go v1.44.116/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.256 h1:O8VH+bJqgLDguqkH/xQBFz5o/YheeZqgcOYIgsTVWY4= +github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -119,6 +122,7 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/container-storage-interface/spec v1.7.0 h1:gW8eyFQUZWWrMWa8p1seJ28gwDoN5CVJ4uAbQ+Hdycw= +github.com/container-storage-interface/spec v1.7.0/go.mod h1:JYuzLqr9VVNoDJl44xp/8fmCOvWPDKzuGTwCoklhuqk= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= @@ -165,7 +169,9 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -197,8 +203,10 @@ github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= +github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= @@ -242,6 +250,7 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -269,6 +278,7 @@ github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJ github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -349,10 +359,11 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= @@ -382,6 +393,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/johannesboyne/gofakes3 v0.0.0-20240513200200-99de01ee122d h1:9dIJ/sx3yapvuq3kvTSVQ6UVS2HxfOB4MCwWiH8JcvQ= +github.com/johannesboyne/gofakes3 v0.0.0-20240513200200-99de01ee122d/go.mod h1:AxgWC4DDX54O2WDoQO1Ceabtn6IbktjU/7bigor+66g= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -394,17 +407,18 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -412,6 +426,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -487,9 +502,13 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= @@ -556,6 +575,7 @@ github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3c github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rosedblabs/rosedb/v2 v2.3.2 h1:1O3+hBjDZNZdDqsjAMuY1gdjENkpGdOKJm91HPGUA5Q= github.com/rosedblabs/rosedb/v2 v2.3.2/go.mod h1:F04QtZBuwT2ZOwzic9OpgN8EGYMNyrB99r7lt4w71ck= github.com/rosedblabs/wal v1.3.3 h1:HBZdmvSpgsuw90IQLY80W0Ht+fNmtwJ83hrSAIxV0d4= @@ -563,11 +583,16 @@ github.com/rosedblabs/wal v1.3.3/go.mod h1:wdq54KJUyVTOv1uddMc6Cdh2d/YCIo8yjcwJA github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= +github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/secretflow/kuscia-envoy v0.0.0-20240604074449-2649bf6e27cb h1:l3hdv8NFkbvO2Rshf996FTyEqqSln6f6F2vCYyoxS08= -github.com/secretflow/kuscia-envoy v0.0.0-20240604074449-2649bf6e27cb/go.mod h1:022TT/HpFoj5iYB6Q/XI0NBnqgM5rekwUo6NAY/K8oA= +github.com/secretflow/kuscia-envoy v0.0.0-20240402083426-b0884d002f48 h1:4W2Nx2lalcZebbyytroXk4LltprKzZSBurqD//SMcZ4= +github.com/secretflow/kuscia-envoy v0.0.0-20240402083426-b0884d002f48/go.mod h1:022TT/HpFoj5iYB6Q/XI0NBnqgM5rekwUo6NAY/K8oA= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 h1:WnNuhiq+FOY3jNj6JXFT+eLN3CQ/oPIsDPRanvwsmbI= +github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500/go.mod h1:+njLrG5wSeoG4Ds61rFgEzKvenR2UHbjMoDHsczxly0= github.com/shirou/gopsutil/v3 v3.22.6 h1:FnHOFOh+cYAM0C30P+zysPISzlknLC5Z1G4EAElznfQ= github.com/shirou/gopsutil/v3 v3.22.6/go.mod h1:EdIubSnZhbAvBS1yJ7Xi+AShB/hxwLHOMz4MCYz7yMs= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -578,7 +603,11 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= @@ -620,6 +649,7 @@ github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYa github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -635,6 +665,7 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1 github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -646,10 +677,12 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= gitlab.com/jonas.jasas/condchan v0.0.0-20190210165812-36637ad2b5bc h1:zCsu+odZEHb2f8U8WWhDgY5N5w3JCLHxuCIqVqCsLcQ= gitlab.com/jonas.jasas/condchan v0.0.0-20190210165812-36637ad2b5bc/go.mod h1:4JS8TdA7HSdK+x43waOdTGodqY/VKsj4w+8pWDL0E88= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd/api/v3 v3.5.6 h1:Cy2qx3npLcYqTKqGJzMypnMv2tiRyifZJ17BlWIWA7A= @@ -661,8 +694,11 @@ go.etcd.io/etcd/client/v2 v2.305.6/go.mod h1:BHha8XJGe8vCIBfWBpbBLVZ4QjOIlfoouvO go.etcd.io/etcd/client/v3 v3.5.6 h1:coLs69PWCXE9G4FKquzNaSHrRyMCAXwF+IX1tAPVO8E= go.etcd.io/etcd/client/v3 v3.5.6/go.mod h1:f6GRinRMCsFVv9Ht42EyY7nfsVGwrNO0WEoS2pRKzQk= go.etcd.io/etcd/pkg/v3 v3.5.5 h1:Ablg7T7OkR+AeeeU32kdVhw/AGDsitkKPl7aW73ssjU= +go.etcd.io/etcd/pkg/v3 v3.5.5/go.mod h1:6ksYFxttiUGzC2uxyqiyOEvhAiD0tuIqSZkX3TyPdaE= go.etcd.io/etcd/raft/v3 v3.5.5 h1:Ibz6XyZ60OYyRopu73lLM/P+qco3YtlZMOhnXNS051I= +go.etcd.io/etcd/raft/v3 v3.5.5/go.mod h1:76TA48q03g1y1VpTue92jZLr9lIHKUNcYdZOOGyx8rI= go.etcd.io/etcd/server/v3 v3.5.5 h1:jNjYm/9s+f9A9r6+SC4RvNaz6AqixpOvhrFdT0PvIj0= +go.etcd.io/etcd/server/v3 v3.5.5/go.mod h1:rZ95vDw/jrvsbj9XpTqPrTAB9/kzchVdhRirySPkUBc= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -694,6 +730,7 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= @@ -711,8 +748,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -748,6 +785,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -788,8 +827,11 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -812,6 +854,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -880,12 +923,16 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -895,6 +942,9 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -908,6 +958,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -916,6 +967,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -952,6 +1004,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -961,6 +1015,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1067,12 +1122,15 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/hack/errorcode/gen_error_code_doc.sh b/hack/errorcode/gen_error_code_doc.sh index a7029b31..e9cf4b34 100644 --- a/hack/errorcode/gen_error_code_doc.sh +++ b/hack/errorcode/gen_error_code_doc.sh @@ -23,8 +23,7 @@ RED='\033[31m' DEFAULT_LOCAL_LANGUAGE=zh-CN - -INPUT_GO=$2 +INPUT_PROTO=$2 I18N_FILE_URI=$3 OUTPUT_MD=$4 @@ -33,26 +32,37 @@ I18N_CODE_PREFIX_SUFFIX_DESC=_description I18N_CODE_PREFIX_SUFFIX_SOLUTION=_solution local_language=$DEFAULT_LOCAL_LANGUAGE -usage() { - echo "$(basename "$0") MODE [OPTIONS] -DEPLOY_MODE: - dif Check i18n file lost. - doc gen markdown doc. +# For example: KusciaAPIErrNotAllowed = 405; +# Can match the name of the error code KusciaAPIErrNotAllowed and the value of the error code 405 +kuscia_api_verify_pattern='^[[:space:]]*(KusciaAPIErr[A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=[[:space:]]*([0-9]+)[[:space:]]*;$' +usage() { + echo "$(basename "$0") MODE INPUT_FILE_PATH I18N_FILE_URI OUTPUT_MD [OPTIONS] +MODE: + doc Generated markdown doc. + append Append i18n file lost. + verify Verify the file (i18n-file / error-code) content matches. +INPUT_FILE_PATH: + [FILE URI] Input file . +I18N_FILE_URI: + [FILE URI] i18n file. +OUTPUT_MD: + [OUT FILE] Out markdown file. OPTIONS: - -l i18n language. default is zh-CN - -m i18n file. + -l i18n language. default is zh-CN +For example: + bash hack/errorcode/gen_error_code_doc.sh doc proto/api/v1alpha1/errorcode/errorcode.proto hack/errorcode/i18n/errorcode.zh-CN.toml docs/reference/apis/error_code_cn.md " } function log() { - local log_content=$1 - echo -e "${GREEN}${log_content}${NC}" + local log_content=$1 + echo -e "${GREEN}${log_content}${NC}" } function log_warn() { - local log_content=$1 - echo -e "${RED}${log_content}${NC}" + local log_content=$1 + echo -e "${RED}${log_content}${NC}" } function init_i18n_array() { @@ -68,7 +78,7 @@ function init_i18n_array() { set_value "$const_name" "${const_value}" fi - done < "$i18n_file" + done <"$i18n_file" } function append_dif_i18n_file() { @@ -77,51 +87,51 @@ function append_dif_i18n_file() { while IFS= read -r line; do # Check if the line has a constant in form of "ConstName = value" - if [[ $line =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=[[:space:]]*([0-9]+) ]]; then + if [[ $line =~ ${kuscia_api_verify_pattern} ]]; then const_name=${BASH_REMATCH[1]} const_value=${BASH_REMATCH[2]} - i18n_value_desc=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_DESC}" ) + i18n_value_desc=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_DESC}") if [ -z "$i18n_value_desc" ]; then - echo "# ${const_name}" >> "$I18N_FILE_URI" - echo "${I18N_CODE_PREFIX}${const_value}${I18N_CODE_PREFIX_SUFFIX_DESC} = \"\"" >> "$I18N_FILE_URI" + echo "# ${const_name}" >>"$I18N_FILE_URI" + echo "${I18N_CODE_PREFIX}${const_value}${I18N_CODE_PREFIX_SUFFIX_DESC} = \"\"" >>"$I18N_FILE_URI" fi - i18n_value_solution=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_SOLUTION}" ) + i18n_value_solution=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_SOLUTION}") if [ -z "$i18n_value_solution" ]; then - echo "# ${const_name}" >> "$I18N_FILE_URI" - echo "${I18N_CODE_PREFIX}${const_value}${I18N_CODE_PREFIX_SUFFIX_SOLUTION} = \"\"" >> "$I18N_FILE_URI" + echo "# ${const_name}" >>"$I18N_FILE_URI" + echo "${I18N_CODE_PREFIX}${const_value}${I18N_CODE_PREFIX_SUFFIX_SOLUTION} = \"\"" >>"$I18N_FILE_URI" fi fi - done < "$INPUT_GO" + done <"$INPUT_PROTO" } function verify_i18n_file_completeness() { - local i18n_value_desc - local i18n_value_solution + local i18n_value_desc + local i18n_value_solution # read go file while IFS= read -r line; do # Check if the line has a constant in form of "ConstName = value" - if [[ $line =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=[[:space:]]*([0-9]+) ]]; then + if [[ $line =~ ${kuscia_api_verify_pattern} ]]; then const_name=${BASH_REMATCH[1]} const_value=${BASH_REMATCH[2]} - i18n_value_desc=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_DESC}" ) + i18n_value_desc=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_DESC}") if [ -z "${i18n_value_desc}" ]; then log_warn "Missing error code [${I18N_CODE_PREFIX}${const_value}${I18N_CODE_PREFIX_SUFFIX_DESC}] i18n configuration. Please perfect the i18n file configuration, file is ${I18N_FILE_URI}" exit 2 fi - i18n_value_solution=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_SOLUTION}" ) + i18n_value_solution=$(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_SOLUTION}") if [ -z "${i18n_value_solution}" ]; then log_warn "Missing error code [${I18N_CODE_PREFIX}${const_value}${I18N_CODE_PREFIX_SUFFIX_SOLUTION}] i18n configuration. Please perfect the i18n file configuration, file is ${I18N_FILE_URI}" exit 2 fi fi - done < "${INPUT_GO}" - log "read over file: ${INPUT_GO}" + done <"${INPUT_PROTO}" + log "read over file: ${INPUT_PROTO}" } @@ -144,77 +154,73 @@ function gen_markdown_doc() { log "gen markdown doc > $OUTPUT_MD" # Preparing the Markdown file - echo -e "$(get_value "doc_title")" > "$OUTPUT_MD" - echo -e "$(get_value "doc_table_title")" >> "$OUTPUT_MD" - echo "| ----- | ----------- | ----------- |" >> "$OUTPUT_MD" + echo -e "$(get_value "doc_title")" >"$OUTPUT_MD" + echo -e "$(get_value "doc_table_title")" >>"$OUTPUT_MD" + echo "| ----- | ----------- | ----------- |" >>"$OUTPUT_MD" while IFS= read -r line; do - if [[ $line =~ =[[:space:]]*([0-9]+) ]]; then - const_value=${BASH_REMATCH[1]} + + if [[ $line =~ $kuscia_api_verify_pattern ]]; then + const_value=${BASH_REMATCH[2]} # Write to the markdown output file - echo -e "| $const_value | $(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_DESC}" ) | $(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_SOLUTION}" ) |" >> "$OUTPUT_MD" + echo -e "| $const_value | $(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_DESC}") | $(get_value "$I18N_CODE_PREFIX""$const_value""${I18N_CODE_PREFIX_SUFFIX_SOLUTION}") |" >>"$OUTPUT_MD" fi - done < "$INPUT_GO" + done <"$INPUT_PROTO" } -# init i18n file -init_i18n_array "${I18N_FILE_URI}" - mode= case "$1" in -append | doc | verify ) - mode=$1 - shift - ;; + append | doc | verify) + mode=$1 + shift + ;; esac -while getopts 'l:m' option; do - case "$option" in - l) - local_language=$option - usage - exit - ;; - m) - if [ -z "$OPTARG" ]; then - printf "Option -m requires an argument.\n" >&2 - exit 1 - fi - I18N_FILE_URI=$option - usage - exit - ;; - :) - printf "missing argument for -%s\n" "$OPTARG" >&2 - exit 1 - ;; - \?) - printf "illegal option: -%s\n" "$OPTARG" >&2 - exit 1 - ;; - esac +while getopts 'l:h' option; do + case "$option" in + l) + local_language=$option + usage + exit + ;; + h) + usage + exit + ;; + :) + printf "missing argument for -%s\n" "$OPTARG" >&2 + exit 1 + ;; + \?) + printf "illegal option: -%s\n" "$OPTARG" >&2 + exit 1 + ;; + esac done shift $((OPTIND - 1)) +# init i18n file +init_i18n_array "${I18N_FILE_URI}" + case "$mode" in -doc) - gen_markdown_doc - log "Generated Markdown documentation at $OUTPUT_MD" - ;; -append) - append_dif_i18n_file - log "append diff code $I18N_FILE_URI" - ;; -verify) - # append error code empty desc - append_dif_i18n_file - # verify - verify_i18n_file_completeness - log "verify i18n file [${I18N_FILE_URI}] is complete." - ;; -*) - printf "unsupported mode: %s\n" "$mode" >&2 - exit 1 - ;; + doc) + gen_markdown_doc + log "Generated Markdown documentation at $OUTPUT_MD" + ;; + append) + append_dif_i18n_file + log "append diff code $I18N_FILE_URI" + ;; + verify) + # append error code empty desc + append_dif_i18n_file + # verify + verify_i18n_file_completeness + log "verify i18n file [${I18N_FILE_URI}] is complete." + ;; + *) + printf "unsupported mode: %s\n" "$mode" >&2 + exit 1 + ;; esac diff --git a/hack/k8s/autonomy/configmap.yaml b/hack/k8s/autonomy/configmap.yaml index efd6b088..0089a3bf 100644 --- a/hack/k8s/autonomy/configmap.yaml +++ b/hack/k8s/autonomy/configmap.yaml @@ -50,7 +50,6 @@ data: ####### master节点配置 ######### # 数据库连接串,不填默认使用 sqlite (dsnXXXX) dns:// - # 注意: database 名称暂不支持 "-" 特殊字符 datastoreEndpoint: "mysql://user:password@tcp(host:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" # KusciaAPI 以及节点对外网关使用的通信协议, NOTLS/TLS/MTLS protocol: NOTLS diff --git a/hack/k8s/master/configmap.yaml b/hack/k8s/master/configmap.yaml index ceb0834e..82ea3885 100644 --- a/hack/k8s/master/configmap.yaml +++ b/hack/k8s/master/configmap.yaml @@ -16,7 +16,6 @@ data: ####### master节点配置 ######### # 数据库连接串,不填默认使用 - # 注意: database 名称暂不支持 "-" 特殊字符 datastoreEndpoint: "mysql://user:password@tcp(host:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" # KusciaAPI 以及节点对外网关使用的通信协议, NOTLS/TLS/MTLS protocol: NOTLS diff --git a/hack/proto-to-go.sh b/hack/proto-to-go.sh index ee0c98bb..60e93a9e 100755 --- a/hack/proto-to-go.sh +++ b/hack/proto-to-go.sh @@ -22,8 +22,13 @@ set -o pipefail PROTOC=protoc KUSCIA_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P) +PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P) echo "${KUSCIA_ROOT}" +if [ ! -d ${KUSCIA_ROOT}/kuscia ]; then + ln -s ${PROJECT_ROOT} ${KUSCIA_ROOT}/kuscia +fi + PROTO_ROOT_PATH=${KUSCIA_ROOT}/kuscia/proto function pre_install() { diff --git a/pkg/agent/pleg/generic.go b/pkg/agent/pleg/generic.go index 89524abc..e31c0dc6 100644 --- a/pkg/agent/pleg/generic.go +++ b/pkg/agent/pleg/generic.go @@ -131,8 +131,8 @@ func (g *GenericPLEG) Watch() chan *PodLifecycleEvent { } // Start spawns a goroutine to relist periodically. -func (g *GenericPLEG) Start() { - go wait.Until(g.relist, g.relistPeriod, wait.NeverStop) +func (g *GenericPLEG) Start(stopCh <-chan struct{}) { + go wait.Until(g.relist, g.relistPeriod, stopCh) } // Healthy check if PLEG work properly. diff --git a/pkg/agent/pleg/pleg.go b/pkg/agent/pleg/pleg.go index 86b48c7e..ce1c0f82 100644 --- a/pkg/agent/pleg/pleg.go +++ b/pkg/agent/pleg/pleg.go @@ -51,7 +51,7 @@ type PodLifecycleEvent struct { // PodLifecycleEventGenerator contains functions for generating pod life cycle events. type PodLifecycleEventGenerator interface { - Start() + Start(stopCh <-chan struct{}) Watch() chan *PodLifecycleEvent Healthy() (bool, error) } diff --git a/pkg/agent/provider/pod/cri_provider.go b/pkg/agent/provider/pod/cri_provider.go index f5aeef0b..e0fe6517 100644 --- a/pkg/agent/provider/pod/cri_provider.go +++ b/pkg/agent/provider/pod/cri_provider.go @@ -309,7 +309,7 @@ func NewCRIProvider(dep *CRIProviderDependence) (kri.PodProvider, error) { func (cp *CRIProvider) Start(ctx context.Context) error { nlog.Info("Starting CRI provider ...") - cp.pleg.Start() + cp.pleg.Start(ctx.Done()) cp.startGarbageCollection() @@ -449,6 +449,10 @@ func (cp *CRIProvider) makeMounts(pod *v1.Pod, container *v1.Container, podVolum return nil, fmt.Errorf("cannot find volume %q to mount into container %q", mount.Name, container.Name) } + if !filepath.IsAbs(mount.MountPath) { + mount.MountPath = filepath.Join(container.WorkingDir, mount.MountPath) + } + hostPath := vol.HostPath if mount.SubPath != "" { diff --git a/pkg/agent/provider/pod/cri_provider_test.go b/pkg/agent/provider/pod/cri_provider_test.go new file mode 100644 index 00000000..a0168b58 --- /dev/null +++ b/pkg/agent/provider/pod/cri_provider_test.go @@ -0,0 +1,150 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pod + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/secretflow/kuscia/pkg/agent/config" + pkgcontainer "github.com/secretflow/kuscia/pkg/agent/container" + resourcetest "github.com/secretflow/kuscia/pkg/agent/resource/testing" +) + +func createTestCRIProvider(t *testing.T) *CRIProvider { + defaultAgentConfig := config.DefaultStaticAgentConfig() + dep := &CRIProviderDependence{ + Namespace: "default", + NodeIP: "127.0.0.1", + RootDirectory: t.TempDir(), + StdoutDirectory: "var/stdout", + AllowPrivileged: false, + NodeName: "test-node", + EventRecorder: nil, + ResourceManager: resourcetest.FakeResourceManager("default"), + PodStateProvider: nil, + PodSyncHandler: nil, + StatusManager: nil, + Runtime: config.ProcessRuntime, + CRIProviderCfg: &defaultAgentConfig.Provider.CRI, + } + cp, err := NewCRIProvider(dep) + assert.NoError(t, err) + return cp.(*CRIProvider) +} + +func TestCRIProvider_GenerateRunContainerOptions(t *testing.T) { + t.Parallel() + + cp := createTestCRIProvider(t) + pods := createTestPods() + + tests := []struct { + pod *v1.Pod + opts *pkgcontainer.RunContainerOptions + }{ + { + pod: &pods[0], + opts: &pkgcontainer.RunContainerOptions{ + Envs: []pkgcontainer.EnvVar{ + { + Name: "ENV_1", + Value: "VALUE_1", + }, + }, + Mounts: []pkgcontainer.Mount{ + { + Name: "volume-1", + HostPath: "/tmp/config", + ContainerPath: "/home/config", + }, + { + Name: "volume-1", + + HostPath: "/tmp/config", + ContainerPath: "/root/config", + }, + { + Name: "storage", + HostPath: cp.GetStorageDir(), + ContainerPath: cp.GetStorageDir(), + }, + }, + Hostname: "pod-1", + }, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("Test %d", i), func(t *testing.T) { + err := cp.volumeManager.MountVolumesForPod(tt.pod) + assert.NoError(t, err) + opts, _, err := cp.GenerateRunContainerOptions(tt.pod, &tt.pod.Spec.Containers[0], "192.168.0.1", nil) + assert.NoError(t, err) + assert.Equal(t, tt.opts, opts) + }) + } + +} + +func createTestPods() []v1.Pod { + return []v1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-1", + Namespace: "default", + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "container-1", + Env: []v1.EnvVar{ + { + Name: "ENV_1", + Value: "VALUE_1", + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "volume-1", + MountPath: "/home/config", + }, + { + Name: "volume-1", + MountPath: "./config", + }, + }, + WorkingDir: "/root", + }, + }, + Volumes: []v1.Volume{ + { + Name: "volume-1", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/tmp/config", + }, + }, + }, + }, + }, + }, + } + +} diff --git a/pkg/common/constants.go b/pkg/common/constants.go index 7446bd99..c4924cd4 100644 --- a/pkg/common/constants.go +++ b/pkg/common/constants.go @@ -119,10 +119,11 @@ const ( TaskSummaryResourceVersionAnnotationKey = "kuscia.secretflow/tasksummary-resource-version" - JobIDAnnotationKey = "kuscia.secretflow/job-id" - TaskIDAnnotationKey = "kuscia.secretflow/task-id" - TaskAliasAnnotationKey = "kuscia.secretflow/task-alias" - TaskResourceGroupAnnotationKey = "kuscia.secretflow/task-resource-group" + JobIDAnnotationKey = "kuscia.secretflow/job-id" + TaskIDAnnotationKey = "kuscia.secretflow/task-id" + TaskAliasAnnotationKey = "kuscia.secretflow/task-alias" + TaskResourceGroupAnnotationKey = "kuscia.secretflow/task-resource-group" + SelfClusterAsParticipantAnnotationKey = "kuscia.secretflow/self-cluster-as-participant" AccessDomainAnnotationKey = "kuscia.secretflow/access-domain" ProtocolAnnotationKey = "kuscia.secretflow/protocol" @@ -202,6 +203,11 @@ const ( RunModeLite = "lite" ) +const ( + // because master service name is *.master.svc + UnSupportedDomainID string = "master" +) + const ( DefaultSecretBackendName = "default" DefaultSecretBackendType = "mem" @@ -228,7 +234,8 @@ const ( ) const ( - GatewayLiveTimeout = 3 * time.Minute + GatewayLiveTimeout = 3 * time.Minute + GatewayHealthCheckDuration = 15 * time.Second ) const ( diff --git a/pkg/common/error.go b/pkg/common/error.go new file mode 100644 index 00000000..9652c13c --- /dev/null +++ b/pkg/common/error.go @@ -0,0 +1,48 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "fmt" + + spb "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/secretflow/kuscia/proto/api/v1alpha1" +) + +func BuildGrpcErrorf(appStatus *v1alpha1.Status, code codes.Code, format string, a ...interface{}) error { + if appStatus == nil { + return status.Errorf(code, format, a...) + } + + s := &spb.Status{ + Code: int32(code), + } + if len(appStatus.Message) > 0 { + s.Message = appStatus.Message + } else { + s.Message = fmt.Sprintf(format, a...) + } + + if detail, err := anypb.New(appStatus); err != nil { + s.Details = []*anypb.Any{ + detail, + } + } + return status.ErrorProto(s) +} diff --git a/pkg/common/error_test.go b/pkg/common/error_test.go new file mode 100644 index 00000000..9ff982e9 --- /dev/null +++ b/pkg/common/error_test.go @@ -0,0 +1,41 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "testing" + + "github.com/secretflow/kuscia/proto/api/v1alpha1" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/codes" +) + +func TestBuildGrpcErrorf(t *testing.T) { + err := BuildGrpcErrorf(nil, codes.Aborted, "xxxx") + assert.Error(t, err) + assert.Equal(t, "rpc error: code = Aborted desc = xxxx", err.Error()) + + err = BuildGrpcErrorf(&v1alpha1.Status{}, codes.Aborted, "xxxx") + + assert.Error(t, err) + assert.Equal(t, "rpc error: code = Aborted desc = xxxx", err.Error()) + + err = BuildGrpcErrorf(&v1alpha1.Status{ + Message: "tttt", + }, codes.Aborted, "xxxx") + + assert.Error(t, err) + assert.Equal(t, "rpc error: code = Aborted desc = tttt", err.Error()) +} diff --git a/pkg/confmanager/bean/http_server_bean.go b/pkg/confmanager/bean/http_server_bean.go index bbe44d61..d6748873 100644 --- a/pkg/confmanager/bean/http_server_bean.go +++ b/pkg/confmanager/bean/http_server_bean.go @@ -18,12 +18,13 @@ import ( "context" "net/http" + "github.com/gin-gonic/gin" + cmconfig "github.com/secretflow/kuscia/pkg/confmanager/config" "github.com/secretflow/kuscia/pkg/confmanager/handler/httphandler/certificate" "github.com/secretflow/kuscia/pkg/confmanager/handler/httphandler/configuration" "github.com/secretflow/kuscia/pkg/confmanager/interceptor" "github.com/secretflow/kuscia/pkg/confmanager/service" - ecode "github.com/secretflow/kuscia/pkg/datamesh/errorcode" "github.com/secretflow/kuscia/pkg/kusciaapi/handler/httphandler/health" apisvc "github.com/secretflow/kuscia/pkg/kusciaapi/service" "github.com/secretflow/kuscia/pkg/web/api" @@ -33,8 +34,7 @@ import ( "github.com/secretflow/kuscia/pkg/web/framework" "github.com/secretflow/kuscia/pkg/web/framework/beans" "github.com/secretflow/kuscia/pkg/web/framework/router" - - "github.com/gin-gonic/gin" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) type httpServerBean struct { @@ -129,7 +129,7 @@ func (s *httpServerBean) registerGroupRoutes(e framework.ConfBeanRegistry) { // protoDecorator is used to wrap handler. func protoDecorator(e framework.ConfBeanRegistry, handler api.ProtoHandler) gin.HandlerFunc { - return decorator.InterConnProtoDecoratorMaker(int32(ecode.ErrRequestInvalidate), int32(ecode.ErrForUnexpected))(e, handler) + return decorator.InterConnProtoDecoratorMaker(int32(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate), int32(pberrorcode.ErrorCode_DataMeshErrForUnexpected))(e, handler) } func convertToGinConf(conf *cmconfig.ConfManagerConfig) beans.GinBeanConfig { diff --git a/pkg/confmanager/handler/grpchandler/configuration.go b/pkg/confmanager/handler/grpchandler/configuration.go index 8d75c46f..973a2124 100644 --- a/pkg/confmanager/handler/grpchandler/configuration.go +++ b/pkg/confmanager/handler/grpchandler/configuration.go @@ -17,11 +17,11 @@ package grpchandler import ( "context" - "github.com/secretflow/kuscia/pkg/confmanager/errorcode" "github.com/secretflow/kuscia/pkg/confmanager/interceptor" "github.com/secretflow/kuscia/pkg/confmanager/service" "github.com/secretflow/kuscia/pkg/web/utils" "github.com/secretflow/kuscia/proto/api/v1alpha1/confmanager" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) // configurationHandler GRPC Handler @@ -40,7 +40,7 @@ func (h *configurationHandler) CreateConfiguration(ctx context.Context, request tlsCert := interceptor.TLSCertFromGRPCContext(ctx) if tlsCert == nil || len(tlsCert.OrganizationalUnit) == 0 { return &confmanager.CreateConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "require client tls ou"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrRequestInvalidate, "require client tls ou"), }, nil } response := h.configurationService.CreateConfiguration(ctx, request, tlsCert.OrganizationalUnit[0]) @@ -51,7 +51,7 @@ func (h *configurationHandler) QueryConfiguration(ctx context.Context, request * tlsCert := interceptor.TLSCertFromGRPCContext(ctx) if tlsCert == nil || len(tlsCert.OrganizationalUnit) == 0 { return &confmanager.QueryConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "require client tls ou"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrRequestInvalidate, "require client tls ou"), }, nil } response := h.configurationService.QueryConfiguration(ctx, request, tlsCert.OrganizationalUnit[0]) diff --git a/pkg/confmanager/service/certificate.go b/pkg/confmanager/service/certificate.go index 4abba8c4..860830fd 100644 --- a/pkg/confmanager/service/certificate.go +++ b/pkg/confmanager/service/certificate.go @@ -27,11 +27,11 @@ import ( "github.com/google/uuid" - cmerrorcode "github.com/secretflow/kuscia/pkg/confmanager/errorcode" tlsutils "github.com/secretflow/kuscia/pkg/utils/tls" "github.com/secretflow/kuscia/pkg/web/errorcode" "github.com/secretflow/kuscia/pkg/web/utils" "github.com/secretflow/kuscia/proto/api/v1alpha1/confmanager" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) const ( @@ -68,19 +68,19 @@ func (s *certificateService) GenerateKeyCerts(ctx context.Context, request *conf if errs := s.ValidateGenerateKeyCertsRequest(ctx, request); errs != nil { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrRequestInvalidate, errs.String()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrRequestInvalidate, errs.String()), } } if s.certValue == nil || s.certValue.Load() == nil { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "can not find domain cert"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "can not find domain cert"), } } domainCert, ok := s.certValue.Load().(*x509.Certificate) if !ok { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "domain cert is not valid"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "domain cert is not valid"), } } @@ -100,13 +100,13 @@ func (s *certificateService) GenerateKeyCerts(ctx context.Context, request *conf key, cert, err := tlsutils.GenerateX509KeyPairStruct(domainCert, s.privateKey, certTmpl) if err != nil { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "build key certs failed"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "build key certs failed"), } } certEncoded, err := tlsutils.EncodeCert(cert) if err != nil { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "build key certs failed"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "build key certs failed"), } } var keyEncoded string @@ -115,26 +115,26 @@ func (s *certificateService) GenerateKeyCerts(ctx context.Context, request *conf keyEncoded, err = tlsutils.EncodeRsaKeyToPKCS1(key) if err != nil { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "build key certs failed"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "build key certs failed"), } } case KeyTypeForPCKS8: keyEncoded, err = tlsutils.EncodeRsaKeyToPKCS8(key) if err != nil { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "build key certs failed"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "build key certs failed"), } } default: return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "not implemented key encoded form"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "not implemented key encoded form"), } } domainCertEncoded, err := tlsutils.EncodeCert(domainCert) if err != nil { return &confmanager.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(cmerrorcode.ErrGenerateKeyCerts, "build key certs failed"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_ConfManagerErrGenerateKeyCerts, "build key certs failed"), } } diff --git a/pkg/confmanager/service/certificate_test.go b/pkg/confmanager/service/certificate_test.go index 1d23cf9e..3c793449 100644 --- a/pkg/confmanager/service/certificate_test.go +++ b/pkg/confmanager/service/certificate_test.go @@ -21,16 +21,15 @@ import ( "testing" "github.com/secretflow/kuscia/pkg/utils/tls" - "github.com/secretflow/kuscia/pkg/web/asserts" "github.com/secretflow/kuscia/proto/api/v1alpha1/confmanager" "github.com/stretchr/testify/assert" ) -func newNewCertificateService() (ICertificateService, error) { +func newNewCertificateService(t *testing.T) (ICertificateService, error) { key, certBytes, err := tls.CreateCA("test") - asserts.NotNil(err, "create ca failed") + assert.NoError(t, err, "create ca failed") cert, err := x509.ParseCertificate(certBytes) - asserts.NotNil(err, "parse ca cert failed") + assert.NoError(t, err, "parse ca cert failed") dominaCertValue := &atomic.Value{} dominaCertValue.Store(cert) return NewCertificateService(CertConfig{ @@ -41,46 +40,50 @@ func newNewCertificateService() (ICertificateService, error) { func TestNewCertificateService(t *testing.T) { t.Parallel() - certService, err := newNewCertificateService() - asserts.NotNil(err, "new certificate service failed") - asserts.IsNil(certService, "new certificate service return nil") + certService, err := newNewCertificateService(t) + assert.Nil(t, err, "new certificate service failed") + assert.NotNil(t, certService, "new certificate service return nil") } func Test_certificateService_GenerateKeyCerts_PKCS1(t *testing.T) { t.Parallel() - certService, err := newNewCertificateService() - assert.Nil(t, err) - assert.NotNil(t, certService) + certService, err := newNewCertificateService(t) + assert.Nil(t, err, "new certificate service failed") + assert.NotNil(t, certService, "new certificate service return nil") + got := certService.GenerateKeyCerts(context.Background(), &confmanager.GenerateKeyCertsRequest{ CommonName: "test", KeyType: KeyTypeForPCKS1, }) + assert.NotNil(t, got) assert.Equal(t, 0, int(got.Status.Code)) - assert.NotEmpty(t, got.Key) - assert.Equal(t, int(2), len(got.CertChain)) + assert.NotEqual(t, "", got.Key) + assert.Equal(t, 2, len(got.CertChain)) } func Test_certificateService_GenerateKeyCerts_PKCS8(t *testing.T) { t.Parallel() - certService, err := newNewCertificateService() - assert.Nil(t, err) - assert.NotNil(t, certService) + certService, err := newNewCertificateService(t) + assert.Nil(t, err, "new certificate service failed") + assert.NotNil(t, certService, "new certificate service return nil") + got := certService.GenerateKeyCerts(context.Background(), &confmanager.GenerateKeyCertsRequest{ CommonName: "test", KeyType: KeyTypeForPCKS8, }) + assert.NotNil(t, got) assert.Equal(t, 0, int(got.Status.Code)) - assert.NotEmpty(t, got.Key) + assert.NotEqual(t, "", got.Key) assert.Equal(t, 2, len(got.CertChain)) } -func Test_certificateService_ValidateGenerateKeyCertsRequest_PKCS1_Error(t *testing.T) { +func Test_certificateService_ValidateGenerateKeyCertsRequest_Success(t *testing.T) { t.Parallel() - certService, err := newNewCertificateService() - assert.Nil(t, err) - assert.NotNil(t, certService) + certService, err := newNewCertificateService(t) + assert.Nil(t, err, "new certificate service failed") + assert.NotNil(t, certService, "new certificate service return nil") got := certService.ValidateGenerateKeyCertsRequest(context.Background(), &confmanager.GenerateKeyCertsRequest{ CommonName: "test", @@ -90,11 +93,11 @@ func Test_certificateService_ValidateGenerateKeyCertsRequest_PKCS1_Error(t *test assert.Nil(t, got) } -func Test_certificateService_ValidateGenerateKeyCertsRequest_PKCS1(t *testing.T) { +func Test_certificateService_ValidateGenerateKeyCertsRequest_1Error(t *testing.T) { t.Parallel() - certService, err := newNewCertificateService() - assert.Nil(t, err) - assert.NotNil(t, certService) + certService, err := newNewCertificateService(t) + assert.Nil(t, err, "new certificate service failed") + assert.NotNil(t, certService, "new certificate service return nil") got := certService.ValidateGenerateKeyCertsRequest(context.Background(), &confmanager.GenerateKeyCertsRequest{ KeyType: KeyTypeForPCKS1, @@ -104,11 +107,11 @@ func Test_certificateService_ValidateGenerateKeyCertsRequest_PKCS1(t *testing.T) assert.Equal(t, 1, len(*got)) } -func Test_certificateService_ValidateGenerateKeyCertsRequest_3(t *testing.T) { +func Test_certificateService_ValidateGenerateKeyCertsRequest_3Error(t *testing.T) { t.Parallel() - certService, err := newNewCertificateService() - assert.Nil(t, err) - assert.NotNil(t, certService) + certService, err := newNewCertificateService(t) + assert.Nil(t, err, "new certificate service failed") + assert.NotNil(t, certService, "new certificate service return nil") got := certService.ValidateGenerateKeyCertsRequest(context.Background(), &confmanager.GenerateKeyCertsRequest{ KeyType: "123", diff --git a/pkg/confmanager/service/configuration.go b/pkg/confmanager/service/configuration.go index f8062877..ab1983d1 100644 --- a/pkg/confmanager/service/configuration.go +++ b/pkg/confmanager/service/configuration.go @@ -18,11 +18,11 @@ import ( "context" "fmt" - "github.com/secretflow/kuscia/pkg/confmanager/errorcode" "github.com/secretflow/kuscia/pkg/secretbackend" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/web/utils" "github.com/secretflow/kuscia/proto/api/v1alpha1/confmanager" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) const ( @@ -55,27 +55,27 @@ func NewConfigurationService(backend secretbackend.SecretDriver, enableAuth bool func (s *configurationService) CreateConfiguration(ctx context.Context, request *confmanager.CreateConfigurationRequest, ou string) confmanager.CreateConfigurationResponse { if ou == "" { return confmanager.CreateConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "tls ou must not be empty"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrRequestInvalidate, "tls ou must not be empty"), } } if request.Id == "" { return confmanager.CreateConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "confID must not be empty"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrRequestInvalidate, "confID must not be empty"), } } if ou != GroupKuscia { if err := s.backend.Set(s.groupPermissionKey(ou, request.Id), ""); err != nil { return confmanager.CreateConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrCreateConfiguration, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrCreateConfiguration, err.Error()), } } } if err := s.backend.Set(request.Id, request.Content); err != nil { return confmanager.CreateConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrCreateConfiguration, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrCreateConfiguration, err.Error()), } } @@ -88,13 +88,13 @@ func (s *configurationService) CreateConfiguration(ctx context.Context, request func (s *configurationService) QueryConfiguration(ctx context.Context, request *confmanager.QueryConfigurationRequest, ou string) confmanager.QueryConfigurationResponse { if ou == "" { return confmanager.QueryConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "tls ou must not be empty"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrRequestInvalidate, "tls ou must not be empty"), } } if len(request.GetIds()) == 0 { return confmanager.QueryConfigurationResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "ids length must not be zero"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_ConfManagerErrRequestInvalidate, "ids length must not be zero"), } } diff --git a/pkg/controllers/clusterdomainroute/controller.go b/pkg/controllers/clusterdomainroute/controller.go index 88ebb52f..fca1d4c2 100644 --- a/pkg/controllers/clusterdomainroute/controller.go +++ b/pkg/controllers/clusterdomainroute/controller.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package clusterdomainroute import ( @@ -245,7 +245,7 @@ func (c *controller) syncHandler(ctx context.Context, key string) error { return err } - appendLabels := make(map[string]string, 0) + appendLabels := make(map[string]string) appendLabels[common.KusciaSourceKey] = cdr.Spec.Source appendLabels[common.KusciaDestinationKey] = cdr.Spec.Destination if sourceRole == kusciaapisv1alpha1.Partner { diff --git a/pkg/controllers/clusterdomainroute/controller_test.go b/pkg/controllers/clusterdomainroute/controller_test.go index 3265213f..c6380df5 100644 --- a/pkg/controllers/clusterdomainroute/controller_test.go +++ b/pkg/controllers/clusterdomainroute/controller_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package clusterdomainroute import ( diff --git a/pkg/controllers/clusterdomainroute/domain.go b/pkg/controllers/clusterdomainroute/domain.go index 4447cd2d..07574128 100644 --- a/pkg/controllers/clusterdomainroute/domain.go +++ b/pkg/controllers/clusterdomainroute/domain.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package clusterdomainroute import ( diff --git a/pkg/controllers/clusterdomainroute/domainroute.go b/pkg/controllers/clusterdomainroute/domainroute.go index d871b72c..d9fed7f9 100644 --- a/pkg/controllers/clusterdomainroute/domainroute.go +++ b/pkg/controllers/clusterdomainroute/domainroute.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package clusterdomainroute import ( @@ -128,6 +128,11 @@ func (c *controller) syncStatusFromDomainroute(cdr *kusciaapisv1alpha1.ClusterDo } } + if IsReady(&cdr.Status) && IsTokenHeartBeatTimeout(cdr.Status.TokenStatus.DestinationTokens) { + setCondition(&cdr.Status, newCondition(kusciaapisv1alpha1.ClusterDomainRouteReady, corev1.ConditionFalse, "HeartBeatTimeout", "HeartBeatTimeout")) + needUpdate = true + } + if needUpdate { sn := len(cdr.Status.TokenStatus.SourceTokens) dn := len(cdr.Status.TokenStatus.DestinationTokens) @@ -148,6 +153,37 @@ func (c *controller) syncStatusFromDomainroute(cdr *kusciaapisv1alpha1.ClusterDo return false, nil } +func IsTokenHeartBeatTimeout(tokens []kusciaapisv1alpha1.DomainRouteToken) bool { + readyTokens := make([]kusciaapisv1alpha1.DomainRouteToken, 0) + for _, token := range tokens { + if token.IsReady { + readyTokens = append(readyTokens, token) + } + } + // no ready tokens + if len(readyTokens) <= 0 { + return false + } + + // heartbeatTime not exists compatible with lower versions + noHeartbeat := true + for _, token := range readyTokens { + noHeartbeat = noHeartbeat && token.HeartBeatTime.Time.IsZero() + } + if noHeartbeat { + return false + } + + for _, token := range readyTokens { + // heartbeatTime exists and not timeout + if !token.HeartBeatTime.Time.IsZero() && time.Since(token.HeartBeatTime.Time) <= 2*common.GatewayHealthCheckDuration { + return false + } + } + // heartbeatTime exists and all timeout + return true +} + func newCondition(condType kusciaapisv1alpha1.ClusterDomainRouteConditionType, status corev1.ConditionStatus, reason, message string) *kusciaapisv1alpha1.ClusterDomainRouteCondition { now := metav1.Now() return &kusciaapisv1alpha1.ClusterDomainRouteCondition{ diff --git a/pkg/controllers/clusterdomainroute/domainroute_test.go b/pkg/controllers/clusterdomainroute/domainroute_test.go new file mode 100644 index 00000000..c6242dd7 --- /dev/null +++ b/pkg/controllers/clusterdomainroute/domainroute_test.go @@ -0,0 +1,150 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package clusterdomainroute + +import ( + "testing" + "time" + + "gotest.tools/v3/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/secretflow/kuscia/pkg/common" + kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" +) + +func TestTokenHeartBeatTimeout(t *testing.T) { + testCases := []struct { + name string + timeout bool + tokens []kusciaapisv1alpha1.DomainRouteToken + }{ + { + name: "empty tokens", + timeout: false, + tokens: []kusciaapisv1alpha1.DomainRouteToken{}, + }, + { + name: "no ready token exists", + timeout: false, + tokens: []kusciaapisv1alpha1.DomainRouteToken{ + { + IsReady: false, + }, + }, + }, + { + name: "no heartbeat time exists", + timeout: false, + tokens: []kusciaapisv1alpha1.DomainRouteToken{ + { + IsReady: true, + }, + }, + }, + { + name: "heartbeat time exists but no timeout", + timeout: false, + tokens: []kusciaapisv1alpha1.DomainRouteToken{ + { + IsReady: false, + }, + { + IsReady: true, + }, + { + IsReady: true, + HeartBeatTime: metav1.Now(), + }, + }, + }, + { + name: "heartbeat time exists and no timeout exists", + timeout: false, + tokens: []kusciaapisv1alpha1.DomainRouteToken{ + { + IsReady: false, + }, + { + IsReady: true, + }, + { + IsReady: true, + HeartBeatTime: metav1.Now(), + }, + { + IsReady: true, + HeartBeatTime: metav1.NewTime(time.Now().Add(-3 * common.GatewayHealthCheckDuration)), + }, + }, + }, + { + name: "heartbeat time exists and timeout exists", + timeout: true, + tokens: []kusciaapisv1alpha1.DomainRouteToken{ + { + IsReady: false, + }, + { + IsReady: true, + }, + { + IsReady: true, + HeartBeatTime: metav1.NewTime(time.Now().Add(-3 * common.GatewayHealthCheckDuration)), + }, + }, + }, + { + name: "heartbeat time exists and no timeout exists", + timeout: false, + tokens: []kusciaapisv1alpha1.DomainRouteToken{ + { + IsReady: true, + }, + { + IsReady: true, + HeartBeatTime: metav1.Now(), + }, + { + IsReady: true, + HeartBeatTime: metav1.NewTime(time.Now().Add(-3 * common.GatewayHealthCheckDuration)), + }, + }, + }, + { + name: "heartbeat time exists but timeout", + timeout: true, + tokens: []kusciaapisv1alpha1.DomainRouteToken{ + { + IsReady: true, + }, + { + IsReady: false, + HeartBeatTime: metav1.Now(), + }, + { + IsReady: true, + HeartBeatTime: metav1.NewTime(time.Now().Add(-3 * common.GatewayHealthCheckDuration)), + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + assert.Equal(t, IsTokenHeartBeatTimeout(testCase.tokens), testCase.timeout) + }) + } +} diff --git a/pkg/controllers/domainroute/check.go b/pkg/controllers/domainroute/check.go index 07402c1b..c3ece225 100644 --- a/pkg/controllers/domainroute/check.go +++ b/pkg/controllers/domainroute/check.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domainroute import ( diff --git a/pkg/controllers/domainroute/controller.go b/pkg/controllers/domainroute/controller.go index e7b43295..4980745a 100644 --- a/pkg/controllers/domainroute/controller.go +++ b/pkg/controllers/domainroute/controller.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domainroute import ( diff --git a/pkg/controllers/kusciadeployment/reconcile.go b/pkg/controllers/kusciadeployment/reconcile.go index 5b37a0e5..85df5596 100644 --- a/pkg/controllers/kusciadeployment/reconcile.go +++ b/pkg/controllers/kusciadeployment/reconcile.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kusciadeployment import ( diff --git a/pkg/controllers/kusciadeployment/util.go b/pkg/controllers/kusciadeployment/util.go index 65490883..946dcaab 100644 --- a/pkg/controllers/kusciadeployment/util.go +++ b/pkg/controllers/kusciadeployment/util.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kusciadeployment import ( diff --git a/pkg/controllers/kusciajob/controller_test.go b/pkg/controllers/kusciajob/controller_test.go index b962e451..4feb31a2 100644 --- a/pkg/controllers/kusciajob/controller_test.go +++ b/pkg/controllers/kusciajob/controller_test.go @@ -34,7 +34,6 @@ import ( "github.com/secretflow/kuscia/pkg/controllers" kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" - kusciascheme "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/scheme" "github.com/secretflow/kuscia/pkg/utils/nlog" ) @@ -89,7 +88,7 @@ func makeKusciaJob() *kusciaapisv1alpha1.KusciaJob { } func Test_KusciaJobControllerHandleTaskSucceed(t *testing.T) { - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) + t.Parallel() testKusciaJob := makeKusciaJob() aliceNs := &v1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -156,7 +155,7 @@ func waitAndChangeKusciaTaskStatus(t *testing.T, c *Controller, expectTaskCount } func Test_KusciaTaskControllerHandlerTaskFailed(t *testing.T) { - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) + t.Parallel() testKusciaJob := makeKusciaJob() aliceNs := &v1.Namespace{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/controllers/kusciajob/handler/approval.go b/pkg/controllers/kusciajob/handler/approval.go index d674b9f9..69481fd6 100644 --- a/pkg/controllers/kusciajob/handler/approval.go +++ b/pkg/controllers/kusciajob/handler/approval.go @@ -54,11 +54,11 @@ func (h *JobScheduler) handleAwaitingApproval(job *kusciaapisv1alpha1.KusciaJob) // Check whether Auto Approval if ok, _ := common.SelfClusterIsInitiator(h.domainLister, job); !ok { //not initiator if !h.enableWorkloadApprove { + if job.Status.ApproveStatus == nil { + job.Status.ApproveStatus = make(map[string]kusciaapisv1alpha1.JobApprovePhase) + } ownP, _, _ := h.getAllParties(job) for p := range ownP { - if job.Status.ApproveStatus == nil { - job.Status.ApproveStatus = make(map[string]kusciaapisv1alpha1.JobApprovePhase) - } job.Status.ApproveStatus[p] = kusciaapisv1alpha1.JobAccepted needUpdateStatus = true } diff --git a/pkg/controllers/kusciajob/handler/approval_test.go b/pkg/controllers/kusciajob/handler/approval_test.go index 12934eaf..d4df3dbb 100644 --- a/pkg/controllers/kusciajob/handler/approval_test.go +++ b/pkg/controllers/kusciajob/handler/approval_test.go @@ -82,6 +82,7 @@ func setJobApprovalStatus(job *kusciaapisv1alpha1.KusciaJob, testCase int) { } func TestAwaitingApprovalHandler_HandlePhase(t *testing.T) { + t.Parallel() independentJob := makeKusciaJob(KusciaJobForShapeIndependent, kusciaapisv1alpha1.KusciaJobScheduleModeBestEffort, 2, nil) diff --git a/pkg/controllers/kusciajob/handler/cancelled_test.go b/pkg/controllers/kusciajob/handler/cancelled_test.go index fc5df5f3..0458e14f 100644 --- a/pkg/controllers/kusciajob/handler/cancelled_test.go +++ b/pkg/controllers/kusciajob/handler/cancelled_test.go @@ -29,17 +29,16 @@ import ( kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" - kusciascheme "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/scheme" kusciainformers "github.com/secretflow/kuscia/pkg/crd/informers/externalversions" "github.com/secretflow/kuscia/pkg/utils/nlog" ) func TestCancelledHandler_HandlePhase(t *testing.T) { + t.Parallel() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(nlog.Infof) eventBroadcaster.StartRecordingToSink( &typedcorev1.EventSinkImpl{Interface: kubefake.NewSimpleClientset().CoreV1().Events("default")}) - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kuscia-job-controller"}) kubeFakeClient := kubefake.NewSimpleClientset() kubeInformersFactory := kubeinformers.NewSharedInformerFactory(kubeFakeClient, 0) diff --git a/pkg/controllers/kusciajob/handler/failed_test.go b/pkg/controllers/kusciajob/handler/failed_test.go index 8b131cc9..07a0665b 100644 --- a/pkg/controllers/kusciajob/handler/failed_test.go +++ b/pkg/controllers/kusciajob/handler/failed_test.go @@ -30,7 +30,6 @@ import ( "github.com/secretflow/kuscia/pkg/common" kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" - kusciascheme "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/scheme" kusciainformers "github.com/secretflow/kuscia/pkg/crd/informers/externalversions" "github.com/secretflow/kuscia/pkg/utils/nlog" ) @@ -57,11 +56,11 @@ func setJobStage(job *kusciaapisv1alpha1.KusciaJob, testCase int) { } func TestFailedHandler_HandlePhase(t *testing.T) { + t.Parallel() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(nlog.Infof) eventBroadcaster.StartRecordingToSink( &typedcorev1.EventSinkImpl{Interface: kubefake.NewSimpleClientset().CoreV1().Events("default")}) - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kuscia-job-controller"}) kubeFakeClient := kubefake.NewSimpleClientset() kubeInformersFactory := kubeinformers.NewSharedInformerFactory(kubeFakeClient, 0) diff --git a/pkg/controllers/kusciajob/handler/initialized.go b/pkg/controllers/kusciajob/handler/initialized.go index 96a3d02b..c74c4beb 100644 --- a/pkg/controllers/kusciajob/handler/initialized.go +++ b/pkg/controllers/kusciajob/handler/initialized.go @@ -55,15 +55,16 @@ func (h *JobScheduler) handleInitialized(job *kusciaapisv1alpha1.KusciaJob) (nee if !ok { return true, nil } + + ownP, _, _ := h.getAllParties(job) // label job with initiator and interConn parties,To notify the interOp controller handle inter connection job. - hasUpdated, err := h.annotateKusciaJob(job) + hasUpdated, err := h.annotateKusciaJob(job, ownP) if err != nil { return true, err } if hasUpdated { return false, nil } - ownP, _, _ := h.getAllParties(job) if job.Status.StageStatus == nil { job.Status.StageStatus = make(map[string]kusciaapisv1alpha1.JobStagePhase) } diff --git a/pkg/controllers/kusciajob/handler/initialized_test.go b/pkg/controllers/kusciajob/handler/initialized_test.go index 86e08fcb..4e5ff332 100644 --- a/pkg/controllers/kusciajob/handler/initialized_test.go +++ b/pkg/controllers/kusciajob/handler/initialized_test.go @@ -109,6 +109,7 @@ func setDomain(testCase int, nsInformer clientcorev1.NamespaceInformer, domainIn } func TestInitializedHandler_HandlePhase(t *testing.T) { + t.Parallel() independentJob := makeKusciaJob(KusciaJobForShapeIndependent, kusciaapisv1alpha1.KusciaJobScheduleModeBestEffort, 2, nil) independentJob2 := makeKusciaJob(KusciaJobForShapeIndependent, diff --git a/pkg/controllers/kusciajob/handler/pending_test.go b/pkg/controllers/kusciajob/handler/pending_test.go index 261d97f1..f09c8df4 100644 --- a/pkg/controllers/kusciajob/handler/pending_test.go +++ b/pkg/controllers/kusciajob/handler/pending_test.go @@ -82,6 +82,7 @@ func setJobStageStatus(job *kusciaapisv1alpha1.KusciaJob, testCase int) { } func TestPendingHandler_HandlePhase(t *testing.T) { + t.Parallel() independentJob := makeKusciaJob(KusciaJobForShapeIndependent, kusciaapisv1alpha1.KusciaJobScheduleModeBestEffort, 2, nil) diff --git a/pkg/controllers/kusciajob/handler/running.go b/pkg/controllers/kusciajob/handler/running.go index e5241ea2..c1739edd 100644 --- a/pkg/controllers/kusciajob/handler/running.go +++ b/pkg/controllers/kusciajob/handler/running.go @@ -17,6 +17,7 @@ package handler import ( "context" "fmt" + "reflect" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -73,19 +74,24 @@ func (h *RunningHandler) handleRunning(job *kusciaapisv1alpha1.KusciaJob) (needU nlog.Errorf("List job sub-tasks selector: %s", err) return false, err } - // compute current status. // NOTE: We don't believe kusciaJob.TaskStatus, we rebuild it from current sub-task status. // MayBe some tasks have been created, but updateStatus failed Or first task creation has been happened, // but updateStatus is delayed. currentSubTasksStatusWithAlias, currentSubTasksStatusWithID := buildJobSubTaskStatus(subTasks, job) currentJobPhase := jobStatusPhaseFrom(job, currentSubTasksStatusWithAlias) + if updateJobSubTaskStatus(&job.Status, currentSubTasksStatusWithID) { + needUpdateStatus = true + } // compute ready task and push job when needed. readyTask := readyTasksOf(job, currentSubTasksStatusWithAlias) if currentJobPhase != kusciaapisv1alpha1.KusciaJobFailed && currentJobPhase != kusciaapisv1alpha1.KusciaJobSucceeded { willStartTask := willStartTasksOf(job, readyTask, currentSubTasksStatusWithAlias) - willStartKusciaTasks := h.buildWillStartKusciaTask(job, willStartTask) + willStartKusciaTasks, err := h.buildWillStartKusciaTask(job, willStartTask) + if err != nil { + return needUpdateStatus, err + } // then we will start KusciaTask for _, t := range willStartKusciaTasks { nlog.Infof("Create kuscia tasks: %s", t.ObjectMeta.Name) @@ -97,18 +103,17 @@ func (h *RunningHandler) handleRunning(job *kusciaapisv1alpha1.KusciaJob) (needU if k8serrors.IsNotFound(err) { existTask, err = h.kusciaClient.KusciaV1alpha1().KusciaTasks(common.KusciaCrossDomain).Get(context.Background(), t.Name, metav1.GetOptions{}) } - if err != nil { nlog.Errorf("Get exist task %v failed: %v", t.Name, err) - setKusciaJobStatus(now, &job.Status, kusciaapisv1alpha1.KusciaJobFailed, "CreateTaskFailed", err.Error()) + setKusciaJobStatus(now, &job.Status, kusciaapisv1alpha1.KusciaJobFailed, string(kusciaapisv1alpha1.CreateTaskFailed), err.Error()) return true, nil } } - if existTask.Annotations == nil || existTask.Annotations[common.JobIDAnnotationKey] != job.Name { - message := fmt.Sprintf("Failed to create task %v because a task with the same name already exists", t.Name) + if existTask.Labels == nil || existTask.Labels[common.LabelJobUID] != string(job.UID) { + message := fmt.Sprintf("Failed to create task %v because of a task with the same name already exists", t.Name) nlog.Error(message) - setKusciaJobStatus(now, &job.Status, kusciaapisv1alpha1.KusciaJobFailed, "CreateTaskFailed", message) + setKusciaJobStatus(now, &job.Status, kusciaapisv1alpha1.KusciaJobFailed, string(kusciaapisv1alpha1.CreateTaskFailed), message) return true, nil } } else { @@ -119,6 +124,16 @@ func (h *RunningHandler) handleRunning(job *kusciaapisv1alpha1.KusciaJob) (needU } } - needUpdateStatus = buildJobStatus(now, &job.Status, currentJobPhase, currentSubTasksStatusWithID) + if buildJobStatus(now, &job.Status, currentJobPhase) { + needUpdateStatus = true + } return needUpdateStatus, nil } + +func updateJobSubTaskStatus(kjStatus *kusciaapisv1alpha1.KusciaJobStatus, currentSubTasksStatus map[string]kusciaapisv1alpha1.KusciaTaskPhase) bool { + if !reflect.DeepEqual(kjStatus.TaskStatus, currentSubTasksStatus) { + kjStatus.TaskStatus = currentSubTasksStatus + return true + } + return false +} diff --git a/pkg/controllers/kusciajob/handler/running_test.go b/pkg/controllers/kusciajob/handler/running_test.go index 04e35002..61af6026 100644 --- a/pkg/controllers/kusciajob/handler/running_test.go +++ b/pkg/controllers/kusciajob/handler/running_test.go @@ -23,7 +23,6 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" kubefake "k8s.io/client-go/kubernetes/fake" @@ -74,6 +73,7 @@ func setRunningJobStage(job *kusciaapisv1alpha1.KusciaJob, testCase int) { } func TestRunningHandler_HandlePhase(t *testing.T) { + t.Parallel() bestEffortIndependent := makeKusciaJob(KusciaJobForShapeIndependent, kusciaapisv1alpha1.KusciaJobScheduleModeBestEffort, 2, nil) //bestEffortIndependent.Spec.Stage = kusciaapisv1alpha1.JobStartStage @@ -286,7 +286,8 @@ func TestRunningHandler_HandlePhase(t *testing.T) { "b": taskExistAssertFunc, "c": taskExistAssertFunc, }, - }, { + }, + { name: "test running start", fields: fields{ kusciaClient: kusciafake.NewSimpleClientset( @@ -308,7 +309,8 @@ func TestRunningHandler_HandlePhase(t *testing.T) { "c": taskExistAssertFunc, }, testcase: testCaseRunningStart, - }, { + }, + { name: "test running restart", fields: fields{ kusciaClient: kusciafake.NewSimpleClientset( @@ -325,7 +327,8 @@ func TestRunningHandler_HandlePhase(t *testing.T) { wantErr: assert.NoError, wantJobPhase: kusciaapisv1alpha1.KusciaJobRunning, testcase: testCaseRunningRestart, - }, { + }, + { name: "test running suspend", fields: fields{ kusciaClient: kusciafake.NewSimpleClientset( @@ -342,7 +345,8 @@ func TestRunningHandler_HandlePhase(t *testing.T) { wantErr: assert.NoError, wantJobPhase: kusciaapisv1alpha1.KusciaJobSuspended, testcase: testCaseRunningSuspend, - }, { + }, + { name: "test running cancel", fields: fields{ kusciaClient: kusciafake.NewSimpleClientset( @@ -359,7 +363,8 @@ func TestRunningHandler_HandlePhase(t *testing.T) { wantErr: assert.NoError, wantJobPhase: kusciaapisv1alpha1.KusciaJobCancelled, testcase: testCaseRunningCancel, - }, { + }, + { name: "test running stop", fields: fields{ kusciaClient: kusciafake.NewSimpleClientset( @@ -383,6 +388,7 @@ func TestRunningHandler_HandlePhase(t *testing.T) { setRunningJobStage(tt.args.kusciaJob, tt.testcase) kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(tt.fields.kusciaClient, 5*time.Minute) kubeInformerFactory := informers.NewSharedInformerFactory(tt.fields.kubeClient, 5*time.Minute) + nsInformer := kubeInformerFactory.Core().V1().Namespaces() domainInformer := kusciaInformerFactory.Kuscia().V1alpha1().Domains() aliceNs := &corev1.Namespace{ @@ -401,19 +407,14 @@ func TestRunningHandler_HandlePhase(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "alice", }, - Spec: kusciaapisv1alpha1.DomainSpec{}, } bobD := &kusciaapisv1alpha1.Domain{ ObjectMeta: metav1.ObjectMeta{ Name: "bob", }, - Spec: kusciaapisv1alpha1.DomainSpec{}, } - nsInformer.Informer().GetStore().Add(aliceNs) - nsInformer.Informer().GetStore().Add(bobNs) domainInformer.Informer().GetStore().Add(aliceD) domainInformer.Informer().GetStore().Add(bobD) - deps := &Dependencies{ KusciaClient: tt.fields.kusciaClient, KusciaTaskLister: kusciaInformerFactory.Kuscia().V1alpha1().KusciaTasks().Lister(), @@ -425,7 +426,7 @@ func TestRunningHandler_HandlePhase(t *testing.T) { } stopCh := make(<-chan struct{}) go kusciaInformerFactory.Start(stopCh) - cache.WaitForCacheSync(wait.NeverStop, kusciaInformerFactory.Kuscia().V1alpha1().KusciaTasks().Informer().HasSynced) + cache.WaitForCacheSync(stopCh, kusciaInformerFactory.Kuscia().V1alpha1().KusciaTasks().Informer().HasSynced) gotNeedUpdate, err := h.HandlePhase(tt.args.kusciaJob) if !tt.wantErr(t, err, fmt.Sprintf("HandlePhase(%v)", tt.args.kusciaJob)) { diff --git a/pkg/controllers/kusciajob/handler/scheduler.go b/pkg/controllers/kusciajob/handler/scheduler.go index 63eba057..e705f968 100644 --- a/pkg/controllers/kusciajob/handler/scheduler.go +++ b/pkg/controllers/kusciajob/handler/scheduler.go @@ -12,13 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package handler import ( "context" "fmt" - "reflect" "sort" "strconv" "strings" @@ -77,7 +76,7 @@ func (h *JobScheduler) handleStageCommand(now metav1.Time, job *kusciaapisv1alph nlog.Infof("Handle stage trigger: %s send command: %s.", cmdTrigger, stageCmd) switch kusciaapisv1alpha1.JobStage(stageCmd) { case kusciaapisv1alpha1.JobStartStage: - return h.handleStageCmdStart(now, job) + return h.handleStageCmdStart(job) case kusciaapisv1alpha1.JobStopStage: return h.handleStageCmdStop(now, job) case kusciaapisv1alpha1.JobCancelStage: @@ -91,7 +90,7 @@ func (h *JobScheduler) handleStageCommand(now metav1.Time, job *kusciaapisv1alph } // handleStageCmdStart handles job-start stage. -func (h *JobScheduler) handleStageCmdStart(now metav1.Time, job *kusciaapisv1alpha1.KusciaJob) (hasReconciled bool, err error) { +func (h *JobScheduler) handleStageCmdStart(job *kusciaapisv1alpha1.KusciaJob) (hasReconciled bool, err error) { // get own party ownP, _, _ := h.getAllParties(job) if job.Status.StageStatus == nil { @@ -116,10 +115,17 @@ func (h *JobScheduler) handleStageCmdRestart(now metav1.Time, job *kusciaapisv1a // print command log cmd, cmdTrigger, _ := h.getStageCmd(job) nlog.Infof("Job: %s party: %s execute the cmd: %s.", job.Name, cmdTrigger, cmd) + // check if job is ValidateFailed + validateCond, exist := utilsres.GetKusciaJobCondition(&job.Status, kusciaapisv1alpha1.JobValidated, false) + if exist && validateCond.Status == corev1.ConditionFalse { + nlog.Infof("Job %s condition %q status is false, skip restarting it", job.Name, kusciaapisv1alpha1.JobValidated) + return false, nil + } // handle 'failed' and 'suspend' phase if job.Status.Phase == kusciaapisv1alpha1.KusciaJobFailed || job.Status.Phase == kusciaapisv1alpha1.KusciaJobSuspended { // set job phase to 'running' job.Status.Phase = kusciaapisv1alpha1.KusciaJobRunning + job.Status.CompletionTime = nil partyStage := kusciaapisv1alpha1.JobRestartStageSucceeded job.Status.Message = fmt.Sprintf("This job is restarted by %s", cmdTrigger) job.Status.Reason = fmt.Sprintf("Party: %s execute the cmd: %s.", cmdTrigger, cmd) @@ -290,8 +296,8 @@ func (h *JobScheduler) validateJob(now metav1.Time, job *kusciaapisv1alpha1.Kusc return true, true } // validate failed - utilsres.SetKusciaJobCondition(now, jobValidatedCond, corev1.ConditionFalse, "ValidateFailed", fmt.Sprintf("Validate job failed, %v", err.Error())) - setKusciaJobStatus(now, &job.Status, kusciaapisv1alpha1.KusciaJobFailed, "KusciaJobValidateFailed", "") + utilsres.SetKusciaJobCondition(now, jobValidatedCond, corev1.ConditionFalse, string(v1alpha1.ValidateFailed), fmt.Sprintf("Validate job failed, %v", err.Error())) + setKusciaJobStatus(now, &job.Status, kusciaapisv1alpha1.KusciaJobFailed, string(v1alpha1.ValidateFailed), "") return true, false } @@ -412,7 +418,6 @@ func (h *JobScheduler) isAllPartyRestartSuccess(job *kusciaapisv1alpha1.KusciaJo } func (h *JobScheduler) isAllPartyRestartComplete(job *kusciaapisv1alpha1.KusciaJob) bool { - for _, party := range h.getParties(job) { stageStatus, ok := job.Status.StageStatus[party.DomainID] if !ok { @@ -477,7 +482,11 @@ func (h *JobScheduler) stopTasks(now metav1.Time, kusciaJob *kusciaapisv1alpha1. if utilsres.IsOuterBFIAInterConnDomain(h.namespaceLister, party.DomainID) { continue } - if utilsres.IsPartnerDomain(h.namespaceLister, party.DomainID) { + isPartner, err := utilsres.IsPartnerDomain(h.namespaceLister, party.DomainID) + if err != nil { + return err + } + if isPartner { continue } found := false @@ -539,20 +548,6 @@ func (h *JobScheduler) kusciaJobValidate(kusciaJob *kusciaapisv1alpha1.KusciaJob return fmt.Errorf("kuscia job should include at least one of task") } - findInitiator := false - for _, party := range kusciaJob.Spec.Tasks[0].Parties { - if _, err := h.namespaceLister.Get(party.DomainID); err != nil { - return fmt.Errorf("can't find party namespace %v under cluster, %v", party.DomainID, err) - } - - if party.DomainID == kusciaJob.Spec.Initiator { - findInitiator = true - } - } - if !findInitiator { - return fmt.Errorf("initiator should be one of task parties") - } - if err := kusciaJobDependenciesExits(kusciaJob); err != nil { return err } @@ -563,11 +558,20 @@ func (h *JobScheduler) kusciaJobValidate(kusciaJob *kusciaapisv1alpha1.KusciaJob // update labels and annotations value // 1 inter connection protocol // 2 SelfClusterAsInitiator -func (h *JobScheduler) annotateKusciaJob(job *kusciaapisv1alpha1.KusciaJob) (hasUpdate bool, err error) { +func (h *JobScheduler) annotateKusciaJob(job *kusciaapisv1alpha1.KusciaJob, ownParties map[string]kusciaapisv1alpha1.Party) (hasUpdate bool, err error) { if job.Annotations != nil { if _, ok := job.Annotations[common.InterConnSelfPartyAnnotationKey]; ok { return false, nil } + + // for job which initiator doesn't participate + // and will not annotate kuscia.secretflow/interconn-self-parties + // so, choose kuscia.secretflow/self-cluster-as-initiator to decide if need return + if len(ownParties) == 0 { + if _, ok := job.Annotations[common.SelfClusterAsInitiatorAnnotationKey]; ok { + return false, nil + } + } } // annotate SelfClusterAsInitiator true or false if err = h.annotateSelfClusterAsInitiator(job); err != nil { @@ -628,6 +632,7 @@ func (h *JobScheduler) annotateInterConn(job *kusciaapisv1alpha1.KusciaJob) (err kusciaDomainList []string selfDomainList []string ) + for _, party := range h.getParties(job) { ns, err := h.namespaceLister.Get(party.DomainID) if err != nil { @@ -824,6 +829,9 @@ func buildJobSubTaskStatus(currentSubTasks []*kusciaapisv1alpha1.KusciaTask, job for idx := range currentSubTasks { for _, task := range job.Spec.Tasks { if task.TaskID == currentSubTasks[idx].Name { + if currentSubTasks[idx].Status.Phase == "" { + currentSubTasks[idx].Status.Phase = kusciaapisv1alpha1.TaskPending + } subTaskStatusWithAlias[task.Alias] = currentSubTasks[idx].Status.Phase subTaskStatusWithID[task.TaskID] = currentSubTasks[idx].Status.Phase } @@ -835,26 +843,20 @@ func buildJobSubTaskStatus(currentSubTasks []*kusciaapisv1alpha1.KusciaTask, job // buildJobStatus builds kuscia job status. func buildJobStatus(now metav1.Time, kjStatus *kusciaapisv1alpha1.KusciaJobStatus, - currentJobStatusPhase kusciaapisv1alpha1.KusciaJobPhase, - currentSubTasksStatus map[string]kusciaapisv1alpha1.KusciaTaskPhase) bool { + currentJobStatusPhase kusciaapisv1alpha1.KusciaJobPhase) bool { needUpdate := false if kjStatus.Phase != currentJobStatusPhase { needUpdate = true kjStatus.Phase = currentJobStatusPhase } - if currentJobStatusPhase == kusciaapisv1alpha1.KusciaJobSucceeded || currentJobStatusPhase == kusciaapisv1alpha1.KusciaJobFailed { + if currentJobStatusPhase == kusciaapisv1alpha1.KusciaJobSucceeded { if kjStatus.CompletionTime == nil { needUpdate = true kjStatus.CompletionTime = &now } } - if !reflect.DeepEqual(kjStatus.TaskStatus, currentSubTasksStatus) { - needUpdate = true - kjStatus.TaskStatus = currentSubTasksStatus - } - return needUpdate } @@ -880,7 +882,7 @@ func jobStatusPhaseFrom(job *kusciaapisv1alpha1.KusciaJob, currentSubTasksStatus // Ready task means it can be scheduled but not scheduled. criticalTasks := tasks.criticalTaskMap() readyTasks := tasks.readyTaskMap() - nlog.Infof("jobStatusPhaseFrom readyTasks=%+v, tasks=%+v, kusciaJobId=%s", + nlog.Infof("JobStatusPhaseFrom readyTasks=%+v, tasks=%+v, kusciaJobId=%s", readyTasks.ToShortString(), tasks.ToShortString(), job.Name) // all subtasks succeed and all critical subtasks succeeded means the job is succeeded. @@ -895,12 +897,20 @@ func jobStatusPhaseFrom(job *kusciaapisv1alpha1.KusciaJob, currentSubTasksStatus return kusciaapisv1alpha1.KusciaJobFailed } case kusciaapisv1alpha1.KusciaJobScheduleModeBestEffort: - // in BestEffort mode, has no readyTask subtasks and running subtasks, and least one critical subtasks is failed. - if len(readyTasks) == 0 && !tasks.AnyMatch(taskRunning) && - criticalTasks.AnyMatch(taskFailed) { - nlog.Infof("jobStatusPhaseFrom failed readyTasks=%+v, tasks=%+v, kusciaJobId=%s", - readyTasks.ToShortString(), tasks.ToShortString(), job.Name) - return kusciaapisv1alpha1.KusciaJobFailed + // if job is interconn type, any subtasks failed means the job is failed. + if isInterConnJob(job) { + if tasks.AnyMatch(taskFailed) { + nlog.Infof("Interconn jobStatusPhaseFrom failed readyTasks=%+v, tasks=%+v, kusciaJobId=%s", + readyTasks.ToShortString(), tasks.ToShortString(), job.Name) + return kusciaapisv1alpha1.KusciaJobFailed + } + } else { + // in BestEffort mode, has no readyTask subtasks and running subtasks, and least one critical subtasks is failed. + if len(readyTasks) == 0 && !tasks.AnyMatch(taskRunning) && criticalTasks.AnyMatch(taskFailed) { + nlog.Infof("JobStatusPhaseFrom failed readyTasks=%+v, tasks=%+v, kusciaJobId=%s", + readyTasks.ToShortString(), tasks.ToShortString(), job.Name) + return kusciaapisv1alpha1.KusciaJobFailed + } } default: // unreachable code, return Failed. Mode will be validated when this job committed. @@ -1003,11 +1013,14 @@ func willStartTasksOf(kusciaJob *kusciaapisv1alpha1.KusciaJob, readyTasks []kusc } // buildWillStartKusciaTask build KusciaTask CR from job's will-start sub-tasks. -func (h *RunningHandler) buildWillStartKusciaTask(kusciaJob *kusciaapisv1alpha1.KusciaJob, willStartTask []kusciaapisv1alpha1.KusciaTaskTemplate) []*kusciaapisv1alpha1.KusciaTask { +func (h *RunningHandler) buildWillStartKusciaTask(kusciaJob *kusciaapisv1alpha1.KusciaJob, willStartTask []kusciaapisv1alpha1.KusciaTaskTemplate) ([]*kusciaapisv1alpha1.KusciaTask, error) { createdTasks := make([]*kusciaapisv1alpha1.KusciaTask, 0) - isIcJob := isInterConnJob(kusciaJob) - for _, t := range willStartTask { + for i, t := range willStartTask { + asParticipant, err := h.selfClusterAsParticipant(&willStartTask[i]) + if err != nil { + return nil, err + } var taskObject = &kusciaapisv1alpha1.KusciaTask{ ObjectMeta: metav1.ObjectMeta{ Name: t.TaskID, @@ -1016,8 +1029,9 @@ func (h *RunningHandler) buildWillStartKusciaTask(kusciaJob *kusciaapisv1alpha1. kusciaapisv1alpha1.SchemeGroupVersion.WithKind(KusciaJobKind)), }, Annotations: map[string]string{ - common.JobIDAnnotationKey: kusciaJob.Name, - common.TaskAliasAnnotationKey: t.Alias, + common.JobIDAnnotationKey: kusciaJob.Name, + common.TaskAliasAnnotationKey: t.Alias, + common.SelfClusterAsParticipantAnnotationKey: strconv.FormatBool(asParticipant), }, Labels: map[string]string{ common.LabelController: LabelControllerValueKusciaJob, @@ -1047,7 +1061,20 @@ func (h *RunningHandler) buildWillStartKusciaTask(kusciaJob *kusciaapisv1alpha1. createdTasks = append(createdTasks, taskObject) } - return createdTasks + return createdTasks, nil +} + +func (h *RunningHandler) selfClusterAsParticipant(task *kusciaapisv1alpha1.KusciaTaskTemplate) (bool, error) { + for _, party := range task.Parties { + isPartner, err := utilsres.IsPartnerDomain(h.namespaceLister, party.DomainID) + if err != nil { + return false, err + } + if !isPartner { + return true, nil + } + } + return false, nil } // createTaskSpec will make kuscia task spec for kuscia job. diff --git a/pkg/controllers/kusciajob/handler/scheduler_test.go b/pkg/controllers/kusciajob/handler/scheduler_test.go index aa1999c0..743b097b 100644 --- a/pkg/controllers/kusciajob/handler/scheduler_test.go +++ b/pkg/controllers/kusciajob/handler/scheduler_test.go @@ -140,6 +140,7 @@ func makeKusciaJob(shape string, mode kusciaapisv1alpha1.KusciaJobScheduleMode, } func Test_kusciaJobValidate(t *testing.T) { + t.Parallel() type args struct { kusciaJob *kusciaapisv1alpha1.KusciaJob } @@ -200,6 +201,7 @@ func Test_kusciaJobValidate(t *testing.T) { } func Test_readyTasksOf(t *testing.T) { + t.Parallel() noDependencies := makeKusciaJob(KusciaJobForShapeIndependent, kusciaapisv1alpha1.KusciaJobScheduleModeBestEffort, 2, nil) linearDependencies := makeKusciaJob(KusciaJobForShapeTree, @@ -317,6 +319,7 @@ func Test_readyTasksOf(t *testing.T) { } func Test_jobStatusPhaseFrom_BestEffort(t *testing.T) { + t.Parallel() type args struct { job *kusciaapisv1alpha1.KusciaJob currentSubTasksStatus map[string]kusciaapisv1alpha1.KusciaTaskPhase @@ -422,6 +425,7 @@ func Test_jobStatusPhaseFrom_BestEffort(t *testing.T) { } func Test_jobStatusPhaseFrom_BestEffort_TolerableB(t *testing.T) { + t.Parallel() type args struct { job *kusciaapisv1alpha1.KusciaJob currentSubTasksStatus map[string]kusciaapisv1alpha1.KusciaTaskPhase @@ -541,6 +545,7 @@ func Test_jobStatusPhaseFrom_BestEffort_TolerableB(t *testing.T) { } func Test_jobStatusPhaseFrom_Strict(t *testing.T) { + t.Parallel() type args struct { job *kusciaapisv1alpha1.KusciaJob currentSubTasksStatus map[string]kusciaapisv1alpha1.KusciaTaskPhase @@ -671,6 +676,7 @@ func Test_jobStatusPhaseFrom_Strict(t *testing.T) { } func Test_jobStatusPhaseFrom_Strict_TolerableB(t *testing.T) { + t.Parallel() type args struct { job *kusciaapisv1alpha1.KusciaJob currentSubTasksStatus map[string]kusciaapisv1alpha1.KusciaTaskPhase @@ -764,6 +770,7 @@ func Test_jobStatusPhaseFrom_Strict_TolerableB(t *testing.T) { } func Test_willStartTasksOf(t *testing.T) { + t.Parallel() linearDependencies := makeKusciaJob(KusciaJobForShapeTree, kusciaapisv1alpha1.KusciaJobScheduleModeBestEffort, 2, nil) type args struct { @@ -854,6 +861,7 @@ func Test_willStartTasksOf(t *testing.T) { } func TestRunningHandler_buildPartyTemplate(t *testing.T) { + t.Parallel() type args struct { party kusciaapisv1alpha1.Party appImageName string diff --git a/pkg/controllers/kusciajob/handler/succeeded_test.go b/pkg/controllers/kusciajob/handler/succeeded_test.go index 3cc32866..3d4d131f 100644 --- a/pkg/controllers/kusciajob/handler/succeeded_test.go +++ b/pkg/controllers/kusciajob/handler/succeeded_test.go @@ -26,16 +26,15 @@ import ( "k8s.io/client-go/tools/record" kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" - kusciascheme "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/scheme" "github.com/secretflow/kuscia/pkg/utils/nlog" ) func TestSucceededHandler_HandlePhase(t *testing.T) { + t.Parallel() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(nlog.Infof) eventBroadcaster.StartRecordingToSink( &typedcorev1.EventSinkImpl{Interface: kubefake.NewSimpleClientset().CoreV1().Events("default")}) - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kuscia-job-controller"}) type fields struct { recorder record.EventRecorder diff --git a/pkg/controllers/kusciajob/handler/suspended_test.go b/pkg/controllers/kusciajob/handler/suspended_test.go index 21dadf32..6d4eb24e 100644 --- a/pkg/controllers/kusciajob/handler/suspended_test.go +++ b/pkg/controllers/kusciajob/handler/suspended_test.go @@ -30,7 +30,6 @@ import ( "github.com/secretflow/kuscia/pkg/common" kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" - kusciascheme "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/scheme" kusciainformers "github.com/secretflow/kuscia/pkg/crd/informers/externalversions" "github.com/secretflow/kuscia/pkg/utils/nlog" ) @@ -57,11 +56,11 @@ func setSuspendJobStage(job *kusciaapisv1alpha1.KusciaJob, testCase int) { } func TestSuspendedHandler_HandlePhase(t *testing.T) { + t.Parallel() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(nlog.Infof) eventBroadcaster.StartRecordingToSink( &typedcorev1.EventSinkImpl{Interface: kubefake.NewSimpleClientset().CoreV1().Events("default")}) - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kuscia-job-controller"}) kubeFakeClient := kubefake.NewSimpleClientset() kubeInformersFactory := kubeinformers.NewSharedInformerFactory(kubeFakeClient, 0) diff --git a/pkg/controllers/kusciatask/controller.go b/pkg/controllers/kusciatask/controller.go index 154aa1f5..82c52676 100644 --- a/pkg/controllers/kusciatask/controller.go +++ b/pkg/controllers/kusciatask/controller.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +// nolint:dulp package kusciatask import ( diff --git a/pkg/controllers/kusciatask/controller_test.go b/pkg/controllers/kusciatask/controller_test.go index 9d3fa208..73663462 100644 --- a/pkg/controllers/kusciatask/controller_test.go +++ b/pkg/controllers/kusciatask/controller_test.go @@ -90,6 +90,7 @@ func makeTestKusciaTask(phase kusciaapisv1alpha1.KusciaTaskPhase) *kusciaapisv1a } func TestNewController(t *testing.T) { + t.Parallel() kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset() eventBroadcaster := record.NewBroadcaster() @@ -104,6 +105,7 @@ func TestNewController(t *testing.T) { } func TestControllerRun(t *testing.T) { + t.Parallel() kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset() eventBroadcaster := record.NewBroadcaster() @@ -123,6 +125,7 @@ func TestControllerRun(t *testing.T) { } func TestEnqueueKusciaTask(t *testing.T) { + t.Parallel() kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset() eventBroadcaster := record.NewBroadcaster() @@ -140,6 +143,7 @@ func TestEnqueueKusciaTask(t *testing.T) { } func TestHandleTaskResourceGroupObject(t *testing.T) { + t.Parallel() kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset() eventBroadcaster := record.NewBroadcaster() @@ -229,6 +233,7 @@ func TestHandleTaskResourceGroupObject(t *testing.T) { } func TestHandlePodObject(t *testing.T) { + t.Parallel() kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset() eventBroadcaster := record.NewBroadcaster() @@ -281,6 +286,7 @@ func TestHandlePodObject(t *testing.T) { } func TestProcessNextWorkItem(t *testing.T) { + t.Parallel() kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset() eventBroadcaster := record.NewBroadcaster() @@ -329,6 +335,7 @@ func TestProcessNextWorkItem(t *testing.T) { } func TestFailKusciaTask(t *testing.T) { + t.Parallel() kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset() eventBroadcaster := record.NewBroadcaster() @@ -348,6 +355,7 @@ func TestFailKusciaTask(t *testing.T) { } func TestUpdateTaskStatus(t *testing.T) { + t.Parallel() pendingKt := makeTestKusciaTask(kusciaapisv1alpha1.TaskPending) runningKt := makeTestKusciaTask(kusciaapisv1alpha1.TaskRunning) kubeFakeClient := kubefake.NewSimpleClientset() @@ -367,6 +375,7 @@ func TestUpdateTaskStatus(t *testing.T) { } func TestName(t *testing.T) { + t.Parallel() kt := makeTestKusciaTask(kusciaapisv1alpha1.TaskRunning) kubeFakeClient := kubefake.NewSimpleClientset() kusciaFakeClient := kusciafake.NewSimpleClientset(kt) diff --git a/pkg/controllers/kusciatask/handler/common.go b/pkg/controllers/kusciatask/handler/common.go index 0c2d55a2..d8227145 100644 --- a/pkg/controllers/kusciatask/handler/common.go +++ b/pkg/controllers/kusciatask/handler/common.go @@ -28,6 +28,7 @@ import ( kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciaclientset "github.com/secretflow/kuscia/pkg/crd/clientset/versioned" kuscialistersv1alpha1 "github.com/secretflow/kuscia/pkg/crd/listers/kuscia/v1alpha1" + utilsres "github.com/secretflow/kuscia/pkg/utils/resources" ) const ( @@ -43,6 +44,24 @@ const ( defaultRetryIntervalSeconds = 30 ) +func selfClusterAsParticipant(namespacesLister corelisters.NamespaceLister, kusciaTask *kusciaapisv1alpha1.KusciaTask) (bool, error) { + if kusciaTask.Annotations != nil && + kusciaTask.Annotations[common.SelfClusterAsParticipantAnnotationKey] == common.True { + return true, nil + } + + for _, party := range kusciaTask.Spec.Parties { + isPartner, err := utilsres.IsPartnerDomain(namespacesLister, party.DomainID) + if err != nil { + return false, err + } + if !isPartner { + return true, nil + } + } + return false, nil +} + func getTaskResourceGroup(ctx context.Context, name string, trgLister kuscialistersv1alpha1.TaskResourceGroupLister, diff --git a/pkg/controllers/kusciatask/handler/failed_handler.go b/pkg/controllers/kusciatask/handler/failed_handler.go index afcbd807..57470203 100644 --- a/pkg/controllers/kusciatask/handler/failed_handler.go +++ b/pkg/controllers/kusciatask/handler/failed_handler.go @@ -47,7 +47,7 @@ func NewFailedHandler(deps *Dependencies, finishedHandler *FinishedHandler) *Fai // Handle is used to perform the real logic. func (h *FailedHandler) Handle(kusciaTask *kusciaapisv1alpha1.KusciaTask) (bool, error) { h.setTaskResourceGroupFailed(kusciaTask) - updateStatuses(kusciaTask) + setPartyTaskStatusFailed(kusciaTask) needUpdate, err := h.FinishedHandler.Handle(kusciaTask) if err != nil { return false, err @@ -79,8 +79,20 @@ func (h *FailedHandler) setTaskResourceGroupFailed(kusciaTask *kusciaapisv1alpha } } -func updateStatuses(kusciaTask *kusciaapisv1alpha1.KusciaTask) { +func setPartyTaskStatusFailed(kusciaTask *kusciaapisv1alpha1.KusciaTask) { // if task failed, set the all party task status to failed + if len(kusciaTask.Status.PartyTaskStatus) == 0 { + for _, party := range kusciaTask.Spec.Parties { + kusciaTask.Status.PartyTaskStatus = append(kusciaTask.Status.PartyTaskStatus, + kusciaapisv1alpha1.PartyTaskStatus{ + DomainID: party.DomainID, + Role: party.Role, + Phase: kusciaapisv1alpha1.TaskFailed, + }) + } + return + } + for idx, status := range kusciaTask.Status.PartyTaskStatus { if status.Phase == kusciaapisv1alpha1.TaskPending || status.Phase == kusciaapisv1alpha1.TaskRunning { kusciaTask.Status.PartyTaskStatus[idx].Phase = kusciaapisv1alpha1.TaskFailed diff --git a/pkg/controllers/kusciatask/handler/failed_handler_test.go b/pkg/controllers/kusciatask/handler/failed_handler_test.go index a5a5a796..0bd4966b 100644 --- a/pkg/controllers/kusciatask/handler/failed_handler_test.go +++ b/pkg/controllers/kusciatask/handler/failed_handler_test.go @@ -23,16 +23,14 @@ import ( "k8s.io/apimachinery/pkg/util/wait" kubeinformers "k8s.io/client-go/informers" kubefake "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/scheme" kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" - kusciascheme "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/scheme" kusciainformers "github.com/secretflow/kuscia/pkg/crd/informers/externalversions" ) func TestFailedHandler_Handle(t *testing.T) { - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) + t.Parallel() kt := &kusciaapisv1alpha1.KusciaTask{ ObjectMeta: metav1.ObjectMeta{ @@ -62,6 +60,7 @@ func TestFailedHandler_Handle(t *testing.T) { } func TestSetTaskResourceGroupFailed(t *testing.T) { + t.Parallel() kt := &kusciaapisv1alpha1.KusciaTask{ ObjectMeta: metav1.ObjectMeta{ Name: "test", diff --git a/pkg/controllers/kusciatask/handler/finished_handler.go b/pkg/controllers/kusciatask/handler/finished_handler.go index 7592b95f..fb979ae8 100644 --- a/pkg/controllers/kusciatask/handler/finished_handler.go +++ b/pkg/controllers/kusciatask/handler/finished_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package handler import ( diff --git a/pkg/controllers/kusciatask/handler/finished_handler_test.go b/pkg/controllers/kusciatask/handler/finished_handler_test.go index 6ea2f3d0..ae1fb5ff 100644 --- a/pkg/controllers/kusciatask/handler/finished_handler_test.go +++ b/pkg/controllers/kusciatask/handler/finished_handler_test.go @@ -32,6 +32,7 @@ import ( ) func TestFinishedHandler_Handle(t *testing.T) { + t.Parallel() testKusciaTask := &kusciaapisv1alpha1.KusciaTask{ ObjectMeta: metav1.ObjectMeta{ Name: "abc", diff --git a/pkg/controllers/kusciatask/handler/pending_handler.go b/pkg/controllers/kusciatask/handler/pending_handler.go index 23d9a3c0..21c6c298 100644 --- a/pkg/controllers/kusciatask/handler/pending_handler.go +++ b/pkg/controllers/kusciatask/handler/pending_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package handler import ( @@ -102,16 +102,16 @@ func (h *PendingHandler) Handle(kusciaTask *kusciaapisv1alpha1.KusciaTask) (need } curKtStatus := kusciaTask.Status.DeepCopy() - refreshKtResourcesStatus(h.kubeClient, h.podsLister, h.servicesLister, curKtStatus) h.initPartyTaskStatus(kusciaTask, curKtStatus) + refreshKtResourcesStatus(h.kubeClient, h.podsLister, h.servicesLister, curKtStatus) if !reflect.DeepEqual(kusciaTask.Status, curKtStatus) { needUpdate = true kusciaTask.Status = *curKtStatus kusciaTask.Status.LastReconcileTime = &now } - if h.taskRunning(now, kusciaTask) { - return true, nil + if updated, err := h.taskRunning(now, kusciaTask); updated || err != nil { + return updated, err } if updated, err := h.taskExpired(now, kusciaTask); updated || err != nil { @@ -146,7 +146,7 @@ func (h *PendingHandler) prepareTaskResources(now metav1.Time, kusciaTask *kusci } if err = h.createTaskResources(kusciaTask); err != nil { - needUpdate = utilsres.SetKusciaTaskCondition(now, cond, v1.ConditionFalse, "KusciaTaskCreateFailed", fmt.Sprintf("Failed to create kusciaTask related resources, %v", err)) + needUpdate = utilsres.SetKusciaTaskCondition(now, cond, v1.ConditionFalse, "KusciaTaskCreateFailed", err.Error()) return needUpdate, err } utilsres.SetKusciaTaskCondition(now, cond, v1.ConditionTrue, "", "") @@ -174,7 +174,11 @@ func (h *PendingHandler) createTaskResources(kusciaTask *kusciaapisv1alpha1.Kusc partyKitInfos[party.DomainID+party.Role] = kit - if !utilsres.IsPartnerDomain(h.namespacesLister, kit.domainID) { + isPartner, err := utilsres.IsPartnerDomain(h.namespacesLister, kit.domainID) + if err != nil { + return err + } + if !isPartner { selfPartyKitInfos[party.DomainID+party.Role] = kit } } @@ -235,7 +239,23 @@ func setPartyTaskStatus(partyInfo kusciaapisv1alpha1.PartyInfo, ktStatus *kuscia }) } -func (h *PendingHandler) taskRunning(now metav1.Time, kusciaTask *kusciaapisv1alpha1.KusciaTask) bool { +func (h *PendingHandler) taskRunning(now metav1.Time, kusciaTask *kusciaapisv1alpha1.KusciaTask) (bool, error) { + // if self cluster is not participant, skip creating related sub resources. + // waiting for other parties to run the task. + asParticipant, err := selfClusterAsParticipant(h.namespacesLister, kusciaTask) + if err != nil { + return false, err + } + if !asParticipant { + for _, status := range kusciaTask.Status.PartyTaskStatus { + if status.Phase != "" && status.Phase != kusciaapisv1alpha1.TaskPending { + kusciaTask.Status.Phase = kusciaapisv1alpha1.TaskRunning + kusciaTask.Status.LastReconcileTime = &now + return true, nil + } + } + } + // Check if there is a pod in running status, // If there is, it indicates that the task status can be converted to running for _, podStatus := range kusciaTask.Status.PodStatuses { @@ -243,10 +263,10 @@ func (h *PendingHandler) taskRunning(now metav1.Time, kusciaTask *kusciaapisv1al if pod != nil && pod.Status.Phase != v1.PodPending { kusciaTask.Status.Phase = kusciaapisv1alpha1.TaskRunning kusciaTask.Status.LastReconcileTime = &now - return true + return true, nil } } - return false + return false, nil } func (h *PendingHandler) taskExpired(now metav1.Time, kusciaTask *kusciaapisv1alpha1.KusciaTask) (bool, error) { @@ -276,6 +296,20 @@ func generateMessageBy(trg *kusciaapisv1alpha1.TaskResourceGroup) string { } func (h *PendingHandler) buildPartyKitInfo(kusciaTask *kusciaapisv1alpha1.KusciaTask, party *kusciaapisv1alpha1.PartyInfo) (*PartyKitInfo, error) { + kit := &PartyKitInfo{ + kusciaTask: kusciaTask, + domainID: party.DomainID, + role: party.Role, + } + + asParticipant, err := selfClusterAsParticipant(h.namespacesLister, kusciaTask) + if err != nil { + return nil, err + } + if !asParticipant { + return kit, nil + } + appImage, err := h.appImagesLister.Get(party.AppImageRef) if err != nil { return nil, fmt.Errorf("failed to get appImage %q from cache, %v", party.AppImageRef, err) @@ -319,24 +353,18 @@ func (h *PendingHandler) buildPartyKitInfo(kusciaTask *kusciaapisv1alpha1.Kuscia minReservedPods = replicas } - kit := &PartyKitInfo{ - kusciaTask: kusciaTask, - domainID: party.DomainID, - role: party.Role, - image: fmt.Sprintf("%s:%s", appImage.Spec.Image.Name, appImage.Spec.Image.Tag), - imageID: appImage.Spec.Image.ID, - deployTemplate: deployTemplate, - configTemplates: appImage.Spec.ConfigTemplates, - servicedPorts: servicedPorts, - minReservedPods: minReservedPods, - pods: pods, - } + kit.image = fmt.Sprintf("%s:%s", appImage.Spec.Image.Name, appImage.Spec.Image.Tag) + kit.imageID = appImage.Spec.Image.ID + kit.deployTemplate = deployTemplate + kit.configTemplates = appImage.Spec.ConfigTemplates + kit.servicedPorts = servicedPorts + kit.minReservedPods = minReservedPods + kit.pods = pods // Todo: Consider how to limit the communication between single-party jobs between multiple parties. if len(kusciaTask.Spec.Parties) > 1 { kit.portAccessDomains = generatePortAccessDomains(kusciaTask.Spec.Parties, deployTemplate.NetworkPolicy, ports) } - return kit, nil } @@ -738,13 +766,16 @@ func (h *PendingHandler) createResourceForParty(partyKit *PartyKitInfo) (map[str } func (h *PendingHandler) createTaskResourceGroup(kusciaTask *kusciaapisv1alpha1.KusciaTask, partyKitInfos map[string]*PartyKitInfo) error { - trg := h.generateTaskResourceGroup(kusciaTask, partyKitInfos) + trg, err := h.generateTaskResourceGroup(kusciaTask, partyKitInfos) + if err != nil { + return err + } return h.submitTaskResourceGroup(trg) } // generateTaskResourceGroup use selfControlled partyKitInfos to create trg.Spec.Parties // and adjust trg.Spec.MinReservedMembers by minus the amount of out of controlled parties -func (h *PendingHandler) generateTaskResourceGroup(kusciaTask *kusciaapisv1alpha1.KusciaTask, partyKitInfos map[string]*PartyKitInfo) *kusciaapisv1alpha1.TaskResourceGroup { +func (h *PendingHandler) generateTaskResourceGroup(kusciaTask *kusciaapisv1alpha1.KusciaTask, partyKitInfos map[string]*PartyKitInfo) (*kusciaapisv1alpha1.TaskResourceGroup, error) { var ( resourceReservedSeconds = defaultResourceReservedSeconds lifeCycleSeconds = defaultLifecycleSeconds @@ -765,12 +796,15 @@ func (h *PendingHandler) generateTaskResourceGroup(kusciaTask *kusciaapisv1alpha var trgParties, outOfControlledParties []kusciaapisv1alpha1.TaskResourceGroupParty for _, partyKitInfo := range partyKitInfos { - isPartner := utilsres.IsPartnerDomain(h.namespacesLister, partyKitInfo.domainID) + isPartner, err := utilsres.IsPartnerDomain(h.namespacesLister, partyKitInfo.domainID) + if err != nil { + return nil, err + } if isPartner { outOfControlledParties = append(outOfControlledParties, kusciaapisv1alpha1.TaskResourceGroupParty{ Role: partyKitInfo.role, DomainID: partyKitInfo.domainID, - MinReservedPods: partyKitInfo.minReservedPods, + MinReservedPods: 1, }) continue } @@ -837,7 +871,7 @@ func (h *PendingHandler) generateTaskResourceGroup(kusciaTask *kusciaapisv1alpha }, } - return trg + return trg, nil } func generateConfigMap(partyKit *PartyKitInfo) *v1.ConfigMap { diff --git a/pkg/controllers/kusciatask/handler/pending_handler_test.go b/pkg/controllers/kusciatask/handler/pending_handler_test.go index 65a673df..bc1d3249 100644 --- a/pkg/controllers/kusciatask/handler/pending_handler_test.go +++ b/pkg/controllers/kusciatask/handler/pending_handler_test.go @@ -76,6 +76,7 @@ func makeTestPendingHandler() *PendingHandler { } func TestPendingHandler_Handle(t *testing.T) { + t.Parallel() handler := makeTestPendingHandler() kusciaTask := makeTestKusciaTaskCase1() @@ -142,6 +143,7 @@ func TestPendingHandler_Handle(t *testing.T) { } func Test_mergeDeployTemplate(t *testing.T) { + t.Parallel() tests := []struct { baseTemplate *kusciaapisv1alpha1.DeployTemplate partyTemplate *kusciaapisv1alpha1.PartyTemplate @@ -161,6 +163,7 @@ func Test_mergeDeployTemplate(t *testing.T) { } func Test_generatePortAccessDomains(t *testing.T) { + t.Parallel() parties := []kusciaapisv1alpha1.PartyInfo{ { DomainID: "domain-a", @@ -260,6 +263,7 @@ func Test_generatePortAccessDomains(t *testing.T) { } func Test_generatePod(t *testing.T) { + t.Parallel() partyKit := &PartyKitInfo{ kusciaTask: makeTestKusciaTaskCase1(), domainID: "domain-a", diff --git a/pkg/controllers/kusciatask/handler/running_handler.go b/pkg/controllers/kusciatask/handler/running_handler.go index 1e3a13cf..790f3e36 100644 --- a/pkg/controllers/kusciatask/handler/running_handler.go +++ b/pkg/controllers/kusciatask/handler/running_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +// nolint:dulp package handler import ( @@ -64,42 +64,45 @@ const ( // RunningHandler is used to handle kuscia task which phase is running. type RunningHandler struct { - kubeClient kubernetes.Interface - kusciaClient kusciaclientset.Interface - trgLister kuscialistersv1alpha1.TaskResourceGroupLister - podsLister corelisters.PodLister - servicesLister corelisters.ServiceLister - nsLister corelisters.NamespaceLister + kubeClient kubernetes.Interface + kusciaClient kusciaclientset.Interface + trgLister kuscialistersv1alpha1.TaskResourceGroupLister + podsLister corelisters.PodLister + servicesLister corelisters.ServiceLister + namespaceLister corelisters.NamespaceLister } // NewRunningHandler returns a RunningHandler instance. func NewRunningHandler(deps *Dependencies) *RunningHandler { return &RunningHandler{ - kubeClient: deps.KubeClient, - kusciaClient: deps.KusciaClient, - trgLister: deps.TrgLister, - podsLister: deps.PodsLister, - servicesLister: deps.ServicesLister, - nsLister: deps.NamespacesLister, + kubeClient: deps.KubeClient, + kusciaClient: deps.KusciaClient, + trgLister: deps.TrgLister, + podsLister: deps.PodsLister, + servicesLister: deps.ServicesLister, + namespaceLister: deps.NamespacesLister, } } // Handle is used to perform the real logic. func (h *RunningHandler) Handle(kusciaTask *kusciaapisv1alpha1.KusciaTask) (bool, error) { now := metav1.Now().Rfc3339Copy() - trg, err := getTaskResourceGroup(context.Background(), kusciaTask.Name, h.trgLister, h.kusciaClient) if err != nil { return false, fmt.Errorf("get task resource group %v failed, %v", kusciaTask.Name, err) } + asParticipant, err := selfClusterAsParticipant(h.namespaceLister, kusciaTask) + if err != nil { + return false, err + } + taskStatus := kusciaTask.Status.DeepCopy() setTaskStatusPhase(taskStatus, trg) - refreshTaskStatus := false if trg.Status.Phase == kusciaapisv1alpha1.TaskResourceGroupPhaseReserved || (trg.Status.Phase == kusciaapisv1alpha1.TaskResourceGroupPhaseReserving && - !utilsres.SelfClusterAsInitiator(h.nsLister, trg.Spec.Initiator, trg.Annotations)) { + (!asParticipant || !utilsres.SelfClusterAsInitiator(h.namespaceLister, trg.Spec.Initiator, trg.Annotations))) { refreshTaskStatus = true } @@ -149,7 +152,6 @@ func (h *RunningHandler) reconcileTaskStatus(taskStatus *kusciaapisv1alpha1.Kusc validPartyCount := len(trg.Spec.Parties) + len(trg.Spec.OutOfControlledParties) minReservedMembers := trg.Spec.MinReservedMembers - if minReservedMembers > validPartyCount-failedPartyCount { taskStatus.Phase = kusciaapisv1alpha1.TaskFailed taskStatus.Message = fmt.Sprintf("The remaining no-failed party task counts %v are less than the threshold %v that meets the conditions for task success. pending party[%v], running party[%v], successful party[%v], failed party[%v]", diff --git a/pkg/controllers/kusciatask/handler/running_handler_test.go b/pkg/controllers/kusciatask/handler/running_handler_test.go index 31b01601..89790bb1 100644 --- a/pkg/controllers/kusciatask/handler/running_handler_test.go +++ b/pkg/controllers/kusciatask/handler/running_handler_test.go @@ -34,6 +34,7 @@ import ( ) func TestRunningHandler_Handle(t *testing.T) { + t.Parallel() trg1 := &kusciaapisv1alpha1.TaskResourceGroup{ ObjectMeta: metav1.ObjectMeta{ Name: "task-1", @@ -270,6 +271,7 @@ func makeNamespace(name string, labels map[string]string) *v1.Namespace { } func TestReconcileTaskStatus(t *testing.T) { + t.Parallel() tests := []struct { name string taskStatus *kusciaapisv1alpha1.KusciaTaskStatus @@ -653,10 +655,10 @@ func TestReconcileTaskStatus(t *testing.T) { podInformer := kubeInformersFactory.Core().V1().Pods() h := &RunningHandler{ - kubeClient: kubeClient, - kusciaClient: kusciaClient, - podsLister: podInformer.Lister(), - nsLister: nsInformer.Lister(), + kubeClient: kubeClient, + kusciaClient: kusciaClient, + podsLister: podInformer.Lister(), + namespaceLister: nsInformer.Lister(), } if tt.pods != nil { diff --git a/pkg/controllers/kusciatask/handler/succeeded_handler_test.go b/pkg/controllers/kusciatask/handler/succeeded_handler_test.go index aa3aaa72..94b85562 100644 --- a/pkg/controllers/kusciatask/handler/succeeded_handler_test.go +++ b/pkg/controllers/kusciatask/handler/succeeded_handler_test.go @@ -22,17 +22,14 @@ import ( "k8s.io/apimachinery/pkg/util/wait" kubeinformers "k8s.io/client-go/informers" kubefake "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/kubernetes/scheme" kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" - kusciascheme "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/scheme" kusciainformers "github.com/secretflow/kuscia/pkg/crd/informers/externalversions" ) func TestSucceededHandler_Handle(t *testing.T) { - assert.NoError(t, kusciascheme.AddToScheme(scheme.Scheme)) - + t.Parallel() testKusciaTask := &kusciaapisv1alpha1.KusciaTask{ ObjectMeta: metav1.ObjectMeta{ UID: "abc", diff --git a/pkg/controllers/options_test.go b/pkg/controllers/options_test.go index eb44946f..e325fbe2 100644 --- a/pkg/controllers/options_test.go +++ b/pkg/controllers/options_test.go @@ -22,6 +22,7 @@ import ( ) func Test_Options(t *testing.T) { + t.Parallel() op := NewOptions() err := op.Validate() assert.NoError(t, err) diff --git a/pkg/controllers/server.go b/pkg/controllers/server.go index 81c9b7b7..4771de4e 100644 --- a/pkg/controllers/server.go +++ b/pkg/controllers/server.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package controllers import ( diff --git a/pkg/controllers/taskresourcegroup/controller.go b/pkg/controllers/taskresourcegroup/controller.go index 774131d8..abe37363 100644 --- a/pkg/controllers/taskresourcegroup/controller.go +++ b/pkg/controllers/taskresourcegroup/controller.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +// nolint:dulp package taskresourcegroup import ( diff --git a/pkg/controllers/taskresourcegroup/controller_test.go b/pkg/controllers/taskresourcegroup/controller_test.go index cbf3d2da..a675e91a 100644 --- a/pkg/controllers/taskresourcegroup/controller_test.go +++ b/pkg/controllers/taskresourcegroup/controller_test.go @@ -33,6 +33,7 @@ import ( ) func TestNewController(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -45,6 +46,7 @@ func TestNewController(t *testing.T) { } func TestHandleAddedTaskResourceGroup(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -102,6 +104,7 @@ func TestHandleAddedTaskResourceGroup(t *testing.T) { } func TestHandleUpdatedTaskResourceGroup(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -184,6 +187,7 @@ func TestHandleUpdatedTaskResourceGroup(t *testing.T) { } func TestHandleDeletedTaskResourceGroup(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -252,6 +256,7 @@ func TestHandleDeletedTaskResourceGroup(t *testing.T) { } func TestResourceFilter(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -305,6 +310,7 @@ func TestResourceFilter(t *testing.T) { } func TestHandleAddedTaskResource(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -360,6 +366,7 @@ func TestHandleAddedTaskResource(t *testing.T) { } func TestHandleUpdatedTaskResource(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -438,6 +445,7 @@ func TestHandleUpdatedTaskResource(t *testing.T) { } func TestHandleAddedPod(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -462,6 +470,7 @@ func TestHandleAddedPod(t *testing.T) { } func TestControllerStop(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ @@ -546,6 +555,7 @@ func TestSyncHandler(t *testing.T) { } func TestHandleReserveFailedTrg(t *testing.T) { + t.Parallel() trg1 := &kusciaapisv1alpha1.TaskResourceGroup{ ObjectMeta: metav1.ObjectMeta{ Name: "trg1", @@ -604,6 +614,7 @@ func TestHandleReserveFailedTrg(t *testing.T) { } func TestName(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), controllers.ControllerConfig{ diff --git a/pkg/controllers/taskresourcegroup/handler/creating_handler.go b/pkg/controllers/taskresourcegroup/handler/creating_handler.go index a13a2569..f6763312 100644 --- a/pkg/controllers/taskresourcegroup/handler/creating_handler.go +++ b/pkg/controllers/taskresourcegroup/handler/creating_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +// nolint:dulp package handler import ( @@ -170,7 +170,10 @@ func (h *CreatingHandler) buildTaskResource(party *kusciaapisv1alpha1.TaskResour condType := kusciaapisv1alpha1.TaskResourceCondReserving condReason := "Create task resource from task resource group" - isPartner := utilsres.IsPartnerDomain(h.namespaceLister, party.DomainID) + isPartner, err := utilsres.IsPartnerDomain(h.namespaceLister, party.DomainID) + if err != nil { + return nil, err + } minReservedPods := getMinReservedPods(party, isPartner) now := metav1.Now() @@ -231,7 +234,11 @@ func (h *CreatingHandler) buildTaskResource(party *kusciaapisv1alpha1.TaskResour // buildTaskResourcePods is used to build task resource pods info. func (h *CreatingHandler) buildTaskResourcePods(namespace string, ps []kusciaapisv1alpha1.TaskResourceGroupPartyPod) ([]kusciaapisv1alpha1.TaskResourcePod, error) { tPods := make([]kusciaapisv1alpha1.TaskResourcePod, 0, len(ps)) - if utilsres.IsPartnerDomain(h.namespaceLister, namespace) { + isPartner, err := utilsres.IsPartnerDomain(h.namespaceLister, namespace) + if err != nil { + return nil, err + } + if isPartner { for _, p := range ps { tPod := kusciaapisv1alpha1.TaskResourcePod{ Name: p.Name, diff --git a/pkg/controllers/taskresourcegroup/handler/creating_handler_test.go b/pkg/controllers/taskresourcegroup/handler/creating_handler_test.go index fcadd873..d4dd1a5d 100644 --- a/pkg/controllers/taskresourcegroup/handler/creating_handler_test.go +++ b/pkg/controllers/taskresourcegroup/handler/creating_handler_test.go @@ -33,6 +33,7 @@ import ( ) func TestNewCreatingHandler(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -54,6 +55,7 @@ func TestNewCreatingHandler(t *testing.T) { } func TestCreatingHandlerHandle(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -68,6 +70,13 @@ func TestCreatingHandlerHandle(t *testing.T) { } trInformer.Informer().GetStore().Add(tr) + ns1 := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns1", + }, + } + nsInformer.Informer().GetStore().Add(ns1) + deps := &Dependencies{ KubeClient: kubeFakeClient, KusciaClient: kusciaFakeClient, @@ -113,6 +122,7 @@ func TestCreatingHandlerHandle(t *testing.T) { } func TestCreateTaskResources(t *testing.T) { + t.Parallel() pod := st.MakePod().Namespace("ns1").Name("pod").Obj() kubeFakeClient := clientsetfake.NewSimpleClientset(pod) kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() @@ -121,9 +131,15 @@ func TestCreateTaskResources(t *testing.T) { podInformer := informerFactory.Core().V1().Pods() nsInformer := informerFactory.Core().V1().Namespaces() trInformer := kusciaInformerFactory.Kuscia().V1alpha1().TaskResources() - podInformer.Informer().GetStore().Add(pod) + ns1 := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns1", + }, + } + nsInformer.Informer().GetStore().Add(ns1) + tr := util.MakeTaskResource("ns1", "tr", 2, nil) tr.Annotations = map[string]string{ common.TaskResourceGroupAnnotationKey: "trg", @@ -146,7 +162,7 @@ func TestCreateTaskResources(t *testing.T) { wantErr bool }{ { - name: "task resource is not exist", + name: "task resource does not exist", trg: &kusciaapisv1alpha1.TaskResourceGroup{ ObjectMeta: metav1.ObjectMeta{ Name: "trg", @@ -181,6 +197,7 @@ func TestCreateTaskResources(t *testing.T) { } func TestBuildTaskResource(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -206,6 +223,13 @@ func TestBuildTaskResource(t *testing.T) { pod1 := st.MakePod().Namespace("ns1").Name("pod1").Containers(containers).Obj() podInformer.Informer().GetStore().Add(pod1) + ns1 := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ns1", + }, + } + nsInformer.Informer().GetStore().Add(ns1) + deps := &Dependencies{ KubeClient: kubeFakeClient, KusciaClient: kusciaFakeClient, @@ -285,6 +309,7 @@ func TestBuildTaskResource(t *testing.T) { } func TestFindPartyTaskResource(t *testing.T) { + t.Parallel() party := kusciaapisv1alpha1.TaskResourceGroupParty{ DomainID: "ns1", Pods: []kusciaapisv1alpha1.TaskResourceGroupPartyPod{ @@ -353,6 +378,7 @@ func TestFindPartyTaskResource(t *testing.T) { } func TestGetMinReservedPods(t *testing.T) { + t.Parallel() party1 := &kusciaapisv1alpha1.TaskResourceGroupParty{ DomainID: "ns1", MinReservedPods: 2, @@ -407,6 +433,7 @@ func TestGetMinReservedPods(t *testing.T) { } func TestGenerateTaskResourceName(t *testing.T) { + t.Parallel() trName := generateTaskResourceName("tr") ret := strings.Split(trName, "-") if len(ret) != 2 { diff --git a/pkg/controllers/taskresourcegroup/handler/factory_test.go b/pkg/controllers/taskresourcegroup/handler/factory_test.go index 06b764b7..0c63fe91 100644 --- a/pkg/controllers/taskresourcegroup/handler/factory_test.go +++ b/pkg/controllers/taskresourcegroup/handler/factory_test.go @@ -31,6 +31,7 @@ import ( ) func TestNewTaskResourceGroupPhaseHandlerFactory(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -59,6 +60,7 @@ func TestNewTaskResourceGroupPhaseHandlerFactory(t *testing.T) { } func TestUpdatePodAnnotations(t *testing.T) { + t.Parallel() pod1 := st.MakePod().Namespace("ns1").Name("pod1"). Annotation(common.TaskResourceGroupAnnotationKey, "trg1"). Label(common.LabelTaskResourceGroupUID, "111").Obj() @@ -99,6 +101,7 @@ func TestUpdatePodAnnotations(t *testing.T) { } func TestPatchTaskResourceStatus(t *testing.T) { + t.Parallel() tr1 := util.MakeTaskResource("ns1", "tr1", 2, nil) tr1.Annotations = map[string]string{ common.TaskResourceGroupAnnotationKey: "trg1", diff --git a/pkg/controllers/taskresourcegroup/handler/failed_handler.go b/pkg/controllers/taskresourcegroup/handler/failed_handler.go index 9754d7af..9f65cffc 100644 --- a/pkg/controllers/taskresourcegroup/handler/failed_handler.go +++ b/pkg/controllers/taskresourcegroup/handler/failed_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package handler import ( diff --git a/pkg/controllers/taskresourcegroup/handler/failed_handler_test.go b/pkg/controllers/taskresourcegroup/handler/failed_handler_test.go index 09089743..a9f3925d 100644 --- a/pkg/controllers/taskresourcegroup/handler/failed_handler_test.go +++ b/pkg/controllers/taskresourcegroup/handler/failed_handler_test.go @@ -28,6 +28,7 @@ import ( ) func TestNewFailedHandler(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) trInformer := kusciaInformerFactory.Kuscia().V1alpha1().TaskResources() @@ -44,6 +45,7 @@ func TestNewFailedHandler(t *testing.T) { } func TestFailedHandlerHandle(t *testing.T) { + t.Parallel() tr1 := util.MakeTaskResource("ns1", "tr1", 2, nil) tr1.Annotations = map[string]string{ common.TaskResourceGroupAnnotationKey: "trg1", diff --git a/pkg/controllers/taskresourcegroup/handler/pending_handler.go b/pkg/controllers/taskresourcegroup/handler/pending_handler.go index 7321577b..0e3c5283 100644 --- a/pkg/controllers/taskresourcegroup/handler/pending_handler.go +++ b/pkg/controllers/taskresourcegroup/handler/pending_handler.go @@ -55,7 +55,6 @@ func (h *PendingHandler) Handle(trg *kusciaapisv1alpha1.TaskResourceGroup) (bool validatedCond, _ := utilsres.GetTaskResourceGroupCondition(&trg.Status, kusciaapisv1alpha1.TaskResourceGroupValidated) utilsres.SetTaskResourceGroupCondition(&now, validatedCond, v1.ConditionTrue, "") if err := validate(trg); err != nil { - nlog.Error(err) trg.Status.Phase = kusciaapisv1alpha1.TaskResourceGroupPhaseFailed utilsres.SetTaskResourceGroupCondition(&now, validatedCond, v1.ConditionFalse, fmt.Sprintf("Validate task resouce group failed, %v", err.Error())) @@ -94,25 +93,6 @@ func validate(trg *kusciaapisv1alpha1.TaskResourceGroup) error { return fmt.Errorf("parties in task resource group %v can't be empty", trg.Name) } - foundInitiator := false - for _, party := range trg.Spec.Parties { - if party.DomainID == trg.Spec.Initiator { - foundInitiator = true - break - } - } - - for _, party := range trg.Spec.OutOfControlledParties { - if party.DomainID == trg.Spec.Initiator { - foundInitiator = true - break - } - } - - if !foundInitiator { - return fmt.Errorf("task resource group initiator %v should be one of parties", trg.Spec.Initiator) - } - return nil } diff --git a/pkg/controllers/taskresourcegroup/handler/pending_handler_test.go b/pkg/controllers/taskresourcegroup/handler/pending_handler_test.go index c767c0e5..329bffb5 100644 --- a/pkg/controllers/taskresourcegroup/handler/pending_handler_test.go +++ b/pkg/controllers/taskresourcegroup/handler/pending_handler_test.go @@ -27,6 +27,7 @@ import ( ) func TestNewPendingHandler(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -48,6 +49,7 @@ func TestNewPendingHandler(t *testing.T) { } func TestPendingHandlerHandle(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -69,23 +71,6 @@ func TestPendingHandlerHandle(t *testing.T) { trg *kusciaapisv1alpha1.TaskResourceGroup want kusciaapisv1alpha1.TaskResourceGroupPhase }{ - { - name: "trg initiator is invalid", - trg: &kusciaapisv1alpha1.TaskResourceGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: "trg1", - }, - Spec: kusciaapisv1alpha1.TaskResourceGroupSpec{ - Initiator: "alice", - Parties: []kusciaapisv1alpha1.TaskResourceGroupParty{ - { - DomainID: "ns1", - }, - }, - }, - }, - want: kusciaapisv1alpha1.TaskResourceGroupPhaseFailed, - }, { name: "handle trg2 successfully", trg: &kusciaapisv1alpha1.TaskResourceGroup{ diff --git a/pkg/controllers/taskresourcegroup/handler/reserve_failed_handler_test.go b/pkg/controllers/taskresourcegroup/handler/reserve_failed_handler_test.go index cbdf8e16..835e72fe 100644 --- a/pkg/controllers/taskresourcegroup/handler/reserve_failed_handler_test.go +++ b/pkg/controllers/taskresourcegroup/handler/reserve_failed_handler_test.go @@ -30,6 +30,7 @@ import ( ) func TestNewReserveFailedHandler(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -51,6 +52,7 @@ func TestNewReserveFailedHandler(t *testing.T) { } func TestReserveFailedHandlerHandle(t *testing.T) { + t.Parallel() tr1 := util.MakeTaskResource("ns1", "tr1", 2, nil) tr1.Annotations = map[string]string{ common.TaskResourceGroupAnnotationKey: "trg1", diff --git a/pkg/controllers/taskresourcegroup/handler/reserved_handler.go b/pkg/controllers/taskresourcegroup/handler/reserved_handler.go index bc208013..f9f0162c 100644 --- a/pkg/controllers/taskresourcegroup/handler/reserved_handler.go +++ b/pkg/controllers/taskresourcegroup/handler/reserved_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package handler import ( diff --git a/pkg/controllers/taskresourcegroup/handler/reserved_handler_test.go b/pkg/controllers/taskresourcegroup/handler/reserved_handler_test.go index 387e6267..128205d6 100644 --- a/pkg/controllers/taskresourcegroup/handler/reserved_handler_test.go +++ b/pkg/controllers/taskresourcegroup/handler/reserved_handler_test.go @@ -28,6 +28,7 @@ import ( ) func TestNewReservedHandler(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) trInformer := kusciaInformerFactory.Kuscia().V1alpha1().TaskResources() @@ -44,6 +45,7 @@ func TestNewReservedHandler(t *testing.T) { } func TestReservedHandlerHandle(t *testing.T) { + t.Parallel() tr1 := util.MakeTaskResource("ns1", "tr1", 2, nil) tr1.Annotations = map[string]string{ common.TaskResourceGroupAnnotationKey: "trg1", diff --git a/pkg/controllers/taskresourcegroup/handler/reserving_handler_test.go b/pkg/controllers/taskresourcegroup/handler/reserving_handler_test.go index ffd7e7ae..ec77be6a 100644 --- a/pkg/controllers/taskresourcegroup/handler/reserving_handler_test.go +++ b/pkg/controllers/taskresourcegroup/handler/reserving_handler_test.go @@ -31,6 +31,7 @@ import ( ) func TestNewReservingHandler(t *testing.T) { + t.Parallel() kubeFakeClient := clientsetfake.NewSimpleClientset() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() informerFactory := informers.NewSharedInformerFactory(kubeFakeClient, 0) @@ -52,6 +53,7 @@ func TestNewReservingHandler(t *testing.T) { } func TestReservingHandlerHandle(t *testing.T) { + t.Parallel() pod1 := st.MakePod().Namespace("ns1").Name("pod1"). Annotation(common.TaskResourceGroupAnnotationKey, "trg1"). Label(common.LabelTaskResourceGroupUID, "111").Obj() diff --git a/pkg/crd/apis/kuscia/v1alpha1/domainroute_types.go b/pkg/crd/apis/kuscia/v1alpha1/domainroute_types.go index b812b97d..80bf5e31 100644 --- a/pkg/crd/apis/kuscia/v1alpha1/domainroute_types.go +++ b/pkg/crd/apis/kuscia/v1alpha1/domainroute_types.go @@ -102,9 +102,12 @@ type DomainRouteToken struct { // Timestamp representing the time when this revision created. // +optional RevisionTime metav1.Time `json:"revisionTime,omitempty"` - // Timestamp representing the time when this revision expirated. + // Timestamp representing the time when this revision expired. // +optional ExpirationTime metav1.Time `json:"expirationTime,omitempty"` + // Timestamp representing the time when this revision heartbeat. + // +optional + HeartBeatTime metav1.Time `json:"heartBeatTime,omitempty"` // Record effective instances // +optional EffectiveInstances []string `json:"effectiveInstances,omitempty"` diff --git a/pkg/crd/apis/kuscia/v1alpha1/kusciajob_types.go b/pkg/crd/apis/kuscia/v1alpha1/kusciajob_types.go index 04b21f3a..db66875b 100644 --- a/pkg/crd/apis/kuscia/v1alpha1/kusciajob_types.go +++ b/pkg/crd/apis/kuscia/v1alpha1/kusciajob_types.go @@ -265,6 +265,13 @@ type KusciaJobCondition struct { LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"` } +type KusciaJobReason string + +const ( + ValidateFailed KusciaJobReason = "ValidateFailed" + CreateTaskFailed KusciaJobReason = "CreateTaskFailed" +) + // KusciaJobPhase defines current status of this kuscia job. type KusciaJobPhase string diff --git a/pkg/crd/apis/kuscia/v1alpha1/taskresourcegroup_types.go b/pkg/crd/apis/kuscia/v1alpha1/taskresourcegroup_types.go index e32b87b8..4d72644e 100644 --- a/pkg/crd/apis/kuscia/v1alpha1/taskresourcegroup_types.go +++ b/pkg/crd/apis/kuscia/v1alpha1/taskresourcegroup_types.go @@ -73,7 +73,7 @@ type TaskResourceGroupSpec struct { // Initiator represents who initiated the task. Initiator string `json:"initiator"` // Parties represents the parties' whose task resource is controlled by self cluster. - Parties []TaskResourceGroupParty `json:"parties"` + Parties []TaskResourceGroupParty `json:"parties,omitempty"` // Out Of Controlled Parties, namely all partner parties in participant cluster OutOfControlledParties []TaskResourceGroupParty `json:"outOfControlledParties,omitempty"` } diff --git a/pkg/crd/apis/kuscia/v1alpha1/zz_generated.deepcopy.go b/pkg/crd/apis/kuscia/v1alpha1/zz_generated.deepcopy.go index 44b360ef..383d759d 100644 --- a/pkg/crd/apis/kuscia/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/crd/apis/kuscia/v1alpha1/zz_generated.deepcopy.go @@ -1232,6 +1232,7 @@ func (in *DomainRouteToken) DeepCopyInto(out *DomainRouteToken) { *out = *in in.RevisionTime.DeepCopyInto(&out.RevisionTime) in.ExpirationTime.DeepCopyInto(&out.ExpirationTime) + in.HeartBeatTime.DeepCopyInto(&out.HeartBeatTime) if in.EffectiveInstances != nil { in, out := &in.EffectiveInstances, &out.EffectiveInstances *out = make([]string, len(*in)) diff --git a/pkg/datamesh/bean/grpc_server_bean.go b/pkg/datamesh/bean/grpc_server_bean.go index ea9902b4..a24abc81 100644 --- a/pkg/datamesh/bean/grpc_server_bean.go +++ b/pkg/datamesh/bean/grpc_server_bean.go @@ -27,14 +27,16 @@ import ( cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" "github.com/secretflow/kuscia/pkg/datamesh/config" - flight2 "github.com/secretflow/kuscia/pkg/datamesh/flight" - "github.com/secretflow/kuscia/pkg/datamesh/handler/grpchandler" + "github.com/secretflow/kuscia/pkg/datamesh/dmflight" "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/pkg/datamesh/v1handler/grpchandler" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/tls" "github.com/secretflow/kuscia/pkg/web/errorcode" "github.com/secretflow/kuscia/pkg/web/framework" + "github.com/secretflow/kuscia/pkg/web/interceptor" "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) type grpcServerBean struct { @@ -60,6 +62,8 @@ func (s *grpcServerBean) Start(ctx context.Context, e framework.ConfBeanRegistry // init grpc server opts opts := []grpc.ServerOption{ grpc.ConnectionTimeout(time.Duration(s.config.ConnectTimeOut) * time.Second), + grpc.ChainUnaryInterceptor(interceptor.UnaryRecoverInterceptor(pberrorcode.ErrorCode_DataMeshErrForUnexpected)), + grpc.StreamInterceptor(interceptor.StreamRecoverInterceptor(pberrorcode.ErrorCode_DataMeshErrForUnexpected)), } if !s.config.DisableTLS { @@ -73,7 +77,7 @@ func (s *grpcServerBean) Start(ctx context.Context, e framework.ConfBeanRegistry } // listen on grpc port - addr := fmt.Sprintf(":%d", s.config.GRPCPort) + addr := fmt.Sprintf("%s:%d", s.config.ListenAddr, s.config.GRPCPort) lis, err := net.Listen("tcp", addr) if err != nil { nlog.Fatalf("Failed to listen on addr[%s]: %v", addr, err) @@ -88,15 +92,7 @@ func (s *grpcServerBean) Start(ctx context.Context, e framework.ConfBeanRegistry datamesh.RegisterDomainDataSourceServiceServer(server, grpchandler.NewDomainDataSourceHandler(datasourceService)) datamesh.RegisterDomainDataGrantServiceServer(server, grpchandler.NewDomainDataGrantHandler(service.NewDomainDataGrantService(s.config))) - // register flight service - if s.config.ExternalDataProxyList != nil && len(s.config.ExternalDataProxyList) > 0 { - dpConf := &s.config.ExternalDataProxyList[0] - metaSrv, err := flight2.NewMetaServer(domainDataService, datasourceService, dpConf) - if err != nil { - nlog.Fatalf("Failed to create meta server: %v", err) - } - flight.RegisterFlightServiceServer(server, grpchandler.NewFlightMetaHandler(metaSrv)) - } + flight.RegisterFlightServiceServer(server, dmflight.NewDataMeshFlightHandler(domainDataService, datasourceService, s.config.ExternalDataProxyList)) reflection.Register(server) diff --git a/pkg/datamesh/bean/http_server_bean.go b/pkg/datamesh/bean/http_server_bean.go index 11337d05..64e29101 100644 --- a/pkg/datamesh/bean/http_server_bean.go +++ b/pkg/datamesh/bean/http_server_bean.go @@ -22,11 +22,10 @@ import ( cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" dmconfig "github.com/secretflow/kuscia/pkg/datamesh/config" - ecode "github.com/secretflow/kuscia/pkg/datamesh/errorcode" - "github.com/secretflow/kuscia/pkg/datamesh/handler/httphandler/domaindata" - "github.com/secretflow/kuscia/pkg/datamesh/handler/httphandler/domaindatagrant" - "github.com/secretflow/kuscia/pkg/datamesh/handler/httphandler/domaindatasource" "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/pkg/datamesh/v1handler/httphandler/domaindata" + "github.com/secretflow/kuscia/pkg/datamesh/v1handler/httphandler/domaindatagrant" + "github.com/secretflow/kuscia/pkg/datamesh/v1handler/httphandler/domaindatasource" "github.com/secretflow/kuscia/pkg/kusciaapi/handler/httphandler/health" apisvc "github.com/secretflow/kuscia/pkg/kusciaapi/service" "github.com/secretflow/kuscia/pkg/web/api" @@ -37,6 +36,7 @@ import ( "github.com/secretflow/kuscia/pkg/web/framework/beans" frameworkconfig "github.com/secretflow/kuscia/pkg/web/framework/config" "github.com/secretflow/kuscia/pkg/web/framework/router" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) type httpServerBean struct { @@ -51,6 +51,7 @@ func NewHTTPServerBean(config *dmconfig.DataMeshConfig) *httpServerBean { // nol ConfigLoader: &frameworkconfig.FlagEnvConfigLoader{ Source: frameworkconfig.SourceEnv, }, + IP: config.ListenAddr, Port: int(config.HTTPPort), Debug: config.Debug, GinBeanConfig: convertToGinConf(config), @@ -168,15 +169,19 @@ func (s *httpServerBean) registerGroupRoutes(e framework.ConfBeanRegistry) { // protoDecorator is used to wrap handler. func protoDecorator(e framework.ConfBeanRegistry, handler api.ProtoHandler) gin.HandlerFunc { - return decorator.InterConnProtoDecoratorMaker(int32(ecode.ErrRequestInvalidate), int32(ecode.ErrForUnexpected))(e, handler) + return decorator.InterConnProtoDecoratorMaker(int32(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate), int32(pberrorcode.ErrorCode_DataMeshErrForUnexpected))(e, handler) } func convertToGinConf(conf *dmconfig.DataMeshConfig) beans.GinBeanConfig { - var tlsConf = &beans.TLSServerConfig{ - CACert: conf.TLS.RootCA, - ServerCert: conf.TLS.ServerCert, - ServerKey: conf.TLS.ServerKey, + var tlsConf *beans.TLSServerConfig + if !conf.DisableTLS { + tlsConf = &beans.TLSServerConfig{ + CACert: conf.TLS.RootCA, + ServerCert: conf.TLS.ServerCert, + ServerKey: conf.TLS.ServerKey, + } } + return beans.GinBeanConfig{ Logger: nil, ReadTimeout: &conf.ReadTimeout, diff --git a/pkg/datamesh/bean/operator_bean.go b/pkg/datamesh/bean/operator_bean.go index ccd0d466..b6380374 100644 --- a/pkg/datamesh/bean/operator_bean.go +++ b/pkg/datamesh/bean/operator_bean.go @@ -50,7 +50,7 @@ func (b *operatorBean) Validate(errs *errorcode.Errs) { func (b *operatorBean) Init(e framework.ConfBeanRegistry) error { // tls config from config file - nlog.Infof("operatorBean init") + nlog.Infof("OperatorBean init") b.operatorSvc = service.NewOperatorService(b.config, cmservice.Exporter.ConfigurationService()) return nil } diff --git a/pkg/datamesh/commands/root_test.go b/pkg/datamesh/commands/root_test.go new file mode 100644 index 00000000..16cf8a22 --- /dev/null +++ b/pkg/datamesh/commands/root_test.go @@ -0,0 +1,198 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "context" + "crypto/md5" + "crypto/rand" + "crypto/rsa" + "encoding/hex" + "fmt" + "net" + "os" + "path" + "testing" + "time" + + "github.com/apache/arrow/go/v13/arrow" + "github.com/apache/arrow/go/v13/arrow/array" + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/apache/arrow/go/v13/arrow/ipc" + "github.com/google/uuid" + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" + kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" + "github.com/secretflow/kuscia/pkg/datamesh/config" + "github.com/secretflow/kuscia/pkg/datamesh/dmflight" + "github.com/secretflow/kuscia/pkg/utils/network" + "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/pkg/utils/paths" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" +) + +const datameshTestNamespace = "datamesh-test" + +func newDataMeshTestConfig() *config.DataMeshConfig { + conf := config.NewDefaultDataMeshConfig() + + conf.KubeNamespace = datameshTestNamespace + conf.KusciaClient = kusciafake.NewSimpleClientset() + conf.DisableTLS = true + conf.ListenAddr = "127.0.0.1" + conf.GRPCPort, _ = network.BuiltinPortAllocator.Next() + conf.HTTPPort, _ = network.BuiltinPortAllocator.Next() + conf.RootDir = "/tmp" + + conf.DomainKey, _ = rsa.GenerateKey(rand.Reader, 2048) + return conf +} + +func isTCPPortOpened(address string, timeout time.Duration) bool { + conn, err := net.DialTimeout("tcp", address, timeout) + if err != nil { + return false + } + defer conn.Close() + return true +} + +func TestFlightServerStartup(t *testing.T) { + t.Parallel() + conf := newDataMeshTestConfig() + + ctx, cancel := context.WithCancel(context.Background()) + + go func() { + err := Run(ctx, conf, conf.KusciaClient) + assert.NoError(t, err) + }() + + grpcAddress := fmt.Sprintf("127.0.0.1:%d", conf.GRPCPort) + + assert.NoError(t, wait.Poll(time.Millisecond*100, time.Second, func() (bool, error) { + return isTCPPortOpened(grpcAddress, time.Millisecond*20), nil + })) + + toc, c1 := context.WithTimeout(ctx, time.Millisecond*100) + defer c1() + + conn, err := grpc.DialContext(toc, grpcAddress, grpc.WithInsecure()) + assert.NoError(t, err) + defer conn.Close() + + // stop all ports + cancel() +} + +func TestFlightServer_GetFlightInfo_Query_DOGET(t *testing.T) { + t.Parallel() + conf := newDataMeshTestConfig() + + ctx, cancel := context.WithCancel(context.Background()) + + go func() { + err := Run(ctx, conf, conf.KusciaClient) + assert.NoError(t, err) + }() + + grpcAddress := fmt.Sprintf("127.0.0.1:%d", conf.GRPCPort) + + assert.NoError(t, wait.Poll(time.Millisecond*100, time.Second, func() (bool, error) { + return isTCPPortOpened(grpcAddress, time.Millisecond*20), nil + })) + + randstr := md5.Sum([]byte(uuid.New().String())) + testFileName := fmt.Sprintf("testFile_%s.txt", hex.EncodeToString(randstr[:])) + conf.KusciaClient.KusciaV1alpha1().DomainDatas(datameshTestNamespace).Create(ctx, &v1alpha1.DomainData{ + ObjectMeta: v1.ObjectMeta{ + Name: "test-data", + }, + Spec: v1alpha1.DomainDataSpec{ + RelativeURI: testFileName, + Name: "test-data", + Type: "RAW", + DataSource: common.DefaultDataProxyDataSourceID, + Author: datameshTestNamespace, + }, + }, v1.CreateOptions{}) + + dialOpts := network.BuildGrpcOptions(nil) + conn, err := grpc.Dial(grpcAddress, dialOpts...) + assert.NoError(t, err) + defer conn.Close() + + flightClient := flight.NewClientFromConn(conn, nil) + + toc, c1 := context.WithTimeout(ctx, time.Millisecond*1000) + defer c1() + + // [CommandDomainDataQuery] try to read file from dataproxy + fd, err := dmflight.DescForCommand(&datamesh.CommandDomainDataQuery{ + DomaindataId: "test-data", + ContentType: datamesh.ContentType_RAW, + }) + assert.NoError(t, err) + finfo, err := flightClient.GetFlightInfo(toc, fd) + assert.NoError(t, err) + assert.NotNil(t, finfo) + assert.NotEqual(t, 0, len(finfo.Endpoint)) + assert.NotNil(t, finfo.Endpoint[0]) + assert.NotNil(t, finfo.Endpoint[0].Ticket) + assert.NotEqual(t, 0, len(finfo.Endpoint[0].Location)) + assert.NotNil(t, finfo.Endpoint[0].Location[0]) + assert.Equal(t, dmflight.BuiltinFlightServerEndpointURI, finfo.Endpoint[0].Location[0].Uri) + + nlog.Infof("tick %s", string(finfo.Endpoint[0].Ticket.GetTicket())) + + // create datasource test file + localfsDir := path.Join(conf.RootDir, common.DefaultDomainDataSourceLocalFSPath) + assert.NoError(t, paths.EnsureDirectory(localfsDir, true)) + + fp, err := os.Create(path.Join(localfsDir, testFileName)) + assert.NoError(t, err) + defer fp.Close() + _, err = fp.WriteString("hello world!") + assert.NoError(t, err) + + defer os.Remove(path.Join(localfsDir, testFileName)) + + // read file from dataproxy + reader, err := flightClient.DoGet(toc, finfo.Endpoint[0].Ticket) + assert.NoError(t, err) + assert.NotNil(t, reader) + + r, err := flight.NewRecordReader(reader, ipc.WithSchema(dmflight.GenerateBinaryDataArrowSchema())) + assert.NoError(t, err) + assert.True(t, r.Next()) + data := r.Record() + assert.NotNil(t, data) + assert.Equal(t, int64(1), data.NumCols()) + assert.NotNil(t, arrow.IsBaseBinary(data.Columns()[0].DataType().ID())) + + b, ok := data.Columns()[0].(*array.Binary) + assert.True(t, ok) + assert.NotNil(t, b) + + assert.Equal(t, 1, b.Len()) + assert.Equal(t, "hello world!", string(b.Value(0))) + + // stop all ports + cancel() +} diff --git a/pkg/datamesh/config/datamesh_config.go b/pkg/datamesh/config/dmconfig.go similarity index 90% rename from pkg/datamesh/config/datamesh_config.go rename to pkg/datamesh/config/dmconfig.go index 147d9254..30b16c68 100644 --- a/pkg/datamesh/config/datamesh_config.go +++ b/pkg/datamesh/config/dmconfig.go @@ -16,6 +16,7 @@ package config import ( "crypto/rsa" + "github.com/spf13/pflag" kusciaclientset "github.com/secretflow/kuscia/pkg/crd/clientset/versioned" @@ -23,8 +24,16 @@ import ( "github.com/secretflow/kuscia/pkg/web/framework/config" ) +type DataProxyMode string + +const ( + ModeDirect DataProxyMode = "direct" + ModeProxy DataProxyMode = "proxy" +) + type DataMeshConfig struct { RootDir string + ListenAddr string // default is empyt HTTPPort int32 GRPCPort int32 Debug bool @@ -48,6 +57,8 @@ type ExternalDataProxyConfig struct { ClientTLSConfig *kusciaconfig.TLSConfig `yaml:"clientTLSConfig,omitempty"` // DatasourceTypes claims which dataSources proxy by this dataProxy, empty means all types that builtin dataProxy unsupported DataSourceTypes []string `yaml:"dataSourceTypes,omitempty"` + // io mode type: proxy(app --> datamesh --> datasource) or direct(app --> datasource) + Mode string `yaml:"mode,omitempty"` } type DbConfig struct { diff --git a/pkg/datamesh/config/dmconfig_test.go b/pkg/datamesh/config/dmconfig_test.go new file mode 100644 index 00000000..79ee2a7e --- /dev/null +++ b/pkg/datamesh/config/dmconfig_test.go @@ -0,0 +1,32 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDefaultDataMeshConfig(t *testing.T) { + config := NewDefaultDataMeshConfig() + assert.Equal(t, 8070, int(config.HTTPPort)) + assert.Equal(t, 8071, int(config.GRPCPort)) + assert.Equal(t, 5, config.ConnectTimeOut) + assert.Equal(t, 20, config.ReadTimeout) + assert.Equal(t, 20, config.WriteTimeout) + assert.Equal(t, 300, config.IdleTimeout) + assert.False(t, config.DisableTLS) +} diff --git a/pkg/datamesh/dmflight/action_handlers.go b/pkg/datamesh/dmflight/action_handlers.go new file mode 100644 index 00000000..15f76844 --- /dev/null +++ b/pkg/datamesh/dmflight/action_handlers.go @@ -0,0 +1,161 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + + "github.com/apache/arrow/go/v13/arrow/flight" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/proto/api/v1alpha1" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +type DataMeshCustomActionHandlers interface { + DoActionCreateDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) + DoActionQueryDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) + DoActionUpdateDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) + DoActionDeleteDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) + DoActionQueryDomainDataSourceRequest(ctx context.Context, body []byte) (*flight.Result, error) +} + +type dmCustomActionHandlers struct { + domainDataService service.IDomainDataService + domainDataSourceService service.IDomainDataSourceService +} + +func NewDataMeshCustomActionHandlers(domainDataService service.IDomainDataService, dataSourceService service.IDomainDataSourceService) DataMeshCustomActionHandlers { + return &dmCustomActionHandlers{ + domainDataService: domainDataService, + domainDataSourceService: dataSourceService, + } +} + +func (s *dmCustomActionHandlers) DoActionCreateDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) { + request := &datamesh.CreateDomainDataRequest{} + + if err := proto.Unmarshal(body, request); err != nil { + return nil, err + } + + domainDataResp, err := s.createDomainData(ctx, request) + if err != nil { + return nil, err + } + + return PackActionResult(domainDataResp) +} + +func (s *dmCustomActionHandlers) DoActionQueryDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) { + request := &datamesh.QueryDomainDataRequest{} + + if err := proto.Unmarshal(body, request); err != nil { + return nil, err + } + + domainDataResp, err := s.getDomainData(ctx, request.DomaindataId) + if err != nil { + return nil, err + } + + return PackActionResult(domainDataResp) +} + +func (s *dmCustomActionHandlers) DoActionUpdateDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) { + request := &datamesh.UpdateDomainDataRequest{} + + if err := proto.Unmarshal(body, request); err != nil { + return nil, err + } + + domainDataResp := s.domainDataService.UpdateDomainData(ctx, request) + if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { + var appStatus *v1alpha1.Status + if domainDataResp.GetStatus() != nil { + appStatus = domainDataResp.GetStatus() + } + return nil, common.BuildGrpcErrorf(appStatus, codes.Internal, "update domain data with id(%s) fail", request.DomaindataId) + } + + return PackActionResult(domainDataResp) +} + +func (s *dmCustomActionHandlers) DoActionDeleteDomainDataRequest(ctx context.Context, body []byte) (*flight.Result, error) { + request := &datamesh.DeleteDomainDataRequest{} + if err := proto.Unmarshal(body, request); err != nil { + return nil, err + } + + domainDataResp := s.domainDataService.DeleteDomainData(ctx, request) + if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { + var appStatus *v1alpha1.Status + if domainDataResp.GetStatus() != nil { + appStatus = domainDataResp.GetStatus() + } + return nil, common.BuildGrpcErrorf(appStatus, codes.Internal, "delete domain data with id(%s) fail", + request.DomaindataId) + } + + return PackActionResult(domainDataResp) +} + +func (s *dmCustomActionHandlers) DoActionQueryDomainDataSourceRequest(ctx context.Context, body []byte) (*flight.Result, error) { + request := &datamesh.QueryDomainDataSourceRequest{} + domainSourceResp := s.domainDataSourceService.QueryDomainDataSource(ctx, request) + if domainSourceResp == nil || domainSourceResp.GetStatus() == nil || domainSourceResp.GetStatus().GetCode() != 0 { + var appStatus *v1alpha1.Status + if domainSourceResp.GetStatus() != nil { + appStatus = domainSourceResp.GetStatus() + } + return nil, common.BuildGrpcErrorf(appStatus, codes.Internal, "query datasource with id(%s) fail", + request.DatasourceId) + } + + return PackActionResult(domainSourceResp) +} + +func (s *dmCustomActionHandlers) getDomainData(ctx context.Context, id string) (*datamesh.QueryDomainDataResponse, error) { + domainDataReq := &datamesh.QueryDomainDataRequest{ + DomaindataId: id, + } + + domainDataResp := s.domainDataService.QueryDomainData(ctx, domainDataReq) + if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { + var appStatus *v1alpha1.Status + if domainDataResp.GetStatus() != nil { + appStatus = domainDataResp.GetStatus() + } + return nil, common.BuildGrpcErrorf(appStatus, codes.Internal, "query domain data by id(%s) fail", id) + } + return domainDataResp, nil +} + +func (s *dmCustomActionHandlers) createDomainData(ctx context.Context, + domainDataReq *datamesh.CreateDomainDataRequest) (*datamesh.CreateDomainDataResponse, error) { + domainDataResp := s.domainDataService.CreateDomainData(ctx, domainDataReq) + if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { + var appStatus *v1alpha1.Status + if domainDataResp.GetStatus() != nil { + appStatus = domainDataResp.GetStatus() + } + return nil, common.BuildGrpcErrorf(appStatus, codes.Internal, "create domain data with id(%s) fail", + domainDataReq.DomaindataId) + } + return domainDataResp, nil +} diff --git a/pkg/confmanager/errorcode/error_code.go b/pkg/datamesh/dmflight/action_handlers_test.go similarity index 56% rename from pkg/confmanager/errorcode/error_code.go rename to pkg/datamesh/dmflight/action_handlers_test.go index 467f3875..63bb5155 100644 --- a/pkg/confmanager/errorcode/error_code.go +++ b/pkg/datamesh/dmflight/action_handlers_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 Ant Group Co., Ltd. +// Copyright 2024 Ant Group Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,20 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package errorcode +package dmflight import ( - "github.com/secretflow/kuscia/pkg/web/errorcode" -) - -const confManagerErrorCode = errorcode.KusciaErrorCode(2000) -const errorCodeInterval = 100 -const ( - ErrRequestInvalidate = confManagerErrorCode + iota - ErrForUnexpected + "testing" - ErrCreateConfiguration = confManagerErrorCode + errorCodeInterval + iota - ErrQueryConfiguration - - ErrGenerateKeyCerts = confManagerErrorCode + 2*errorCodeInterval + "github.com/stretchr/testify/assert" ) + +func TestNewDataMeshCustomActionHandlers(t *testing.T) { + cah := NewDataMeshCustomActionHandlers(nil, nil) + assert.NotNil(t, cah) +} diff --git a/pkg/datamesh/dmflight/builtin_dataio_direct.go b/pkg/datamesh/dmflight/builtin_dataio_direct.go new file mode 100644 index 00000000..b0fc4441 --- /dev/null +++ b/pkg/datamesh/dmflight/builtin_dataio_direct.go @@ -0,0 +1,47 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + + "github.com/apache/arrow/go/v13/arrow/flight" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/secretflow/kuscia/pkg/datamesh/config" +) + +type builtinDirectIO struct { + config *config.ExternalDataProxyConfig +} + +func NewBuiltinDirectIO(config *config.ExternalDataProxyConfig) DataMeshDataIOInterface { + return &builtinDirectIO{ + config: config, + } +} + +func (dio *builtinDirectIO) Read(ctx context.Context, rc *DataMeshRequestContext, w *flight.Writer) error { + return status.Errorf(codes.Unimplemented, "not implemented") +} + +func (dio *builtinDirectIO) Write(ctx context.Context, rc *DataMeshRequestContext, stream *flight.Reader) error { + return status.Errorf(codes.Unimplemented, "not implemented") +} + +func (dio *builtinDirectIO) GetEndpointURI() string { + return dio.config.Endpoint +} diff --git a/pkg/datamesh/dmflight/builtin_dataio_direct_test.go b/pkg/datamesh/dmflight/builtin_dataio_direct_test.go new file mode 100644 index 00000000..f57d6406 --- /dev/null +++ b/pkg/datamesh/dmflight/builtin_dataio_direct_test.go @@ -0,0 +1,42 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/secretflow/kuscia/pkg/datamesh/config" +) + +func TestNewBuiltinDirectIO(t *testing.T) { + t.Parallel() + channel := NewBuiltinDirectIO(nil) + assert.NotNil(t, channel) + assert.Nil(t, channel.(*builtinDirectIO).config) + + assert.Error(t, channel.Read(context.Background(), nil, nil)) + assert.Error(t, channel.Write(context.Background(), nil, nil)) +} + +func TestDirectChannel_Endpoint(t *testing.T) { + t.Parallel() + channel := NewBuiltinDirectIO(&config.ExternalDataProxyConfig{ + Endpoint: "xyz", + }) + assert.Equal(t, "xyz", channel.GetEndpointURI()) +} diff --git a/pkg/datamesh/dmflight/builtin_dataio_localfile.go b/pkg/datamesh/dmflight/builtin_dataio_localfile.go new file mode 100644 index 00000000..f236782c --- /dev/null +++ b/pkg/datamesh/dmflight/builtin_dataio_localfile.go @@ -0,0 +1,109 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "os" + "path" + + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +type BuiltinLocalFileIO struct { + batchReadSize int +} + +func NewBuiltinLocalFileIOChannel() DataMeshDataIOInterface { + return &BuiltinLocalFileIO{ + batchReadSize: 4096, + } +} + +// DataFlow: RemoteStorage(FileSystem/OSS/...) --> DataProxy --> Client +func (fio *BuiltinLocalFileIO) Read(ctx context.Context, rc *DataMeshRequestContext, w *flight.Writer) error { + data, ds, err := rc.GetDomainDataAndSource(ctx) + + if err != nil { + return err + } + + filePath := path.Join(ds.Info.Localfs.Path, data.RelativeUri) + + if _, err := os.Stat(filePath); err != nil { + nlog.Infof("DomainData(%s) file(%s) stat failed with: %s", data.DomaindataId, filePath, err.Error()) + return err + } + + file, err := os.Open(filePath) + if err != nil { + nlog.Warnf("DomainData(%s) opening file(%s) with error: %s", data.DomaindataId, filePath, err.Error()) + return err + } + defer file.Close() + + switch rc.GetTransferContentType() { + case datamesh.ContentType_RAW: + return DataProxyContentToFlightStreamBinary(data, file, w, fio.batchReadSize) + case datamesh.ContentType_CSV, datamesh.ContentType_Table: + return DataProxyContentToFlightStreamCSV(data, file, w) + default: + return errors.Errorf("invalidate content-type: %s", rc.GetTransferContentType().String()) + } +} + +// DataFlow: Client --> DataProxy --> RemoteStorage(FileSystem/OSS/...) +func (fio *BuiltinLocalFileIO) Write(ctx context.Context, rc *DataMeshRequestContext, reader *flight.Reader) error { + data, ds, err := rc.GetDomainDataAndSource(ctx) + + if err != nil { + return err + } + + filePath := path.Join(ds.Info.Localfs.Path, data.RelativeUri) + + nlog.Infof("DomainData(%s) try save to file(%s)", data.DomaindataId, filePath) + + if _, err := os.Stat(filePath); err == nil { + nlog.Infof("DomainData(%s) file(%s) file exists, can't upload", data.DomaindataId, filePath) + return status.Errorf(codes.AlreadyExists, "file exists, can't upload %s", data.RelativeUri) + } + + file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, 0644) + if err != nil { + nlog.Warnf("DomainData(%s) opening file(%s) to write with error: %s", data.DomaindataId, filePath, err.Error()) + return err + } + defer file.Close() + + switch rc.GetTransferContentType() { + case datamesh.ContentType_RAW: + return FlightStreamToDataProxyContentBinary(data, file, reader) + case datamesh.ContentType_CSV, datamesh.ContentType_Table: + return FlightStreamToDataProxyContentCSV(data, file, reader) + default: + return errors.Errorf("invalidate content-type: %s", rc.GetTransferContentType().String()) + } +} + +func (fio *BuiltinLocalFileIO) GetEndpointURI() string { + return BuiltinFlightServerEndpointURI +} diff --git a/pkg/datamesh/dmflight/builtin_dataio_localfile_test.go b/pkg/datamesh/dmflight/builtin_dataio_localfile_test.go new file mode 100644 index 00000000..77004b3e --- /dev/null +++ b/pkg/datamesh/dmflight/builtin_dataio_localfile_test.go @@ -0,0 +1,252 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "fmt" + "os" + "path" + "testing" + + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/apache/arrow/go/v13/arrow/ipc" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/reflect/protoreflect" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +func TestNewBuiltinLocalFileIOChannel(t *testing.T) { + t.Parallel() + assert.NotNil(t, NewBuiltinLocalFileIOChannel()) +} + +func initLocalFileDataIOTestRequestContext(t *testing.T, filename string, isQuery bool) *DataMeshRequestContext { + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + // init ok + registLocalFileDomainDataSource(t, conf, common.DefaultDataSourceID) + domainDataID := registDomainData(t, conf, common.DefaultDataSourceID, filename) + var msg protoreflect.ProtoMessage + if isQuery { + msg = &datamesh.CommandDomainDataQuery{ + DomaindataId: domainDataID, + } + } else { + msg = &datamesh.CommandDomainDataUpdate{ + DomaindataId: domainDataID, + } + } + ctx, err := NewDataMeshRequestContext(domainDataService, datasourceService, msg) + + assert.NoError(t, err) + assert.NotNil(t, ctx) + return ctx +} + +func TestLocalFileIOChannel_Read_Invalidate(t *testing.T) { + t.Parallel() + channel := NewBuiltinLocalFileIOChannel() + + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + ctx, err := NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataQuery{ + DomaindataId: "not-exists-domain-data", + ContentType: datamesh.ContentType_RAW, + }) + + assert.NoError(t, err) + assert.NotNil(t, ctx) + + assert.Error(t, channel.Read(context.Background(), ctx, nil)) +} + +func TestLocalFileIOChannel_Read_OpenFileFailed(t *testing.T) { + t.Parallel() + filename := fmt.Sprintf("localtest-%s.txt", uuid.New().String()) + ctx := initLocalFileDataIOTestRequestContext(t, filename, true) + + channel := NewBuiltinLocalFileIOChannel() + // file not exists + assert.Error(t, channel.Read(context.Background(), ctx, nil)) + + // invalidate content-type + // write test file + dd, ds, err := ctx.GetDomainDataAndSource(context.Background()) + assert.NoError(t, err) + assert.NotNil(t, ds) + assert.NotNil(t, dd) + + filepath := path.Join(ds.Info.Localfs.Path, dd.RelativeUri) + + file, err := os.OpenFile(filepath, os.O_CREATE|os.O_RDWR, 0777) + assert.NoError(t, err) + assert.NotNil(t, file) + + _, err = file.WriteString("hello world!") + assert.NoError(t, err) + assert.NoError(t, file.Close()) + + ctx.query.ContentType = -1 + assert.Error(t, channel.Read(context.Background(), ctx, nil)) + assert.NoError(t, os.Remove(filepath)) +} + +func TestLocalFileIOChannel_Read_Success(t *testing.T) { + t.Parallel() + filename := fmt.Sprintf("localtest-%s.txt", uuid.New().String()) + + ctx := initLocalFileDataIOTestRequestContext(t, filename, true) + dd, ds, err := ctx.GetDomainDataAndSource(context.Background()) + assert.NoError(t, err) + assert.NotNil(t, ds) + assert.NotNil(t, dd) + + // init test file + filepath := path.Join(ds.Info.Localfs.Path, dd.RelativeUri) + + file, err := os.OpenFile(filepath, os.O_CREATE|os.O_RDWR, 0777) + assert.NoError(t, err) + assert.NotNil(t, file) + + _, err = file.WriteString("hello world!") + assert.NoError(t, err) + assert.NoError(t, file.Close()) + + ctx.query.ContentType = datamesh.ContentType_RAW + + channel := NewBuiltinLocalFileIOChannel() + + mgs := &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + } + writer := flight.NewRecordWriter(mgs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + assert.NotNil(t, writer) + + // write success + assert.NoError(t, channel.Read(context.Background(), ctx, writer)) + + assert.NoError(t, os.Remove(filepath)) +} + +func TestLocalFileIOChannel_Write_Invalidate(t *testing.T) { + channel := NewBuiltinLocalFileIOChannel() + + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + ctx, err := NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataQuery{ + DomaindataId: "not-exists-domain-data", + ContentType: datamesh.ContentType_RAW, + }) + + assert.NoError(t, err) + assert.NotNil(t, ctx) + + assert.Error(t, channel.Write(context.Background(), ctx, nil)) +} + +func TestLocalFileIOChannel_Write_OpenFileFailed(t *testing.T) { + t.Parallel() + filename := fmt.Sprintf("localtest-%s.txt", uuid.New().String()) + ctx := initLocalFileDataIOTestRequestContext(t, filename, false) + + channel := NewBuiltinLocalFileIOChannel() + // file exists + + dd, ds, err := ctx.GetDomainDataAndSource(context.Background()) + assert.NoError(t, err) + assert.NotNil(t, ds) + assert.NotNil(t, dd) + + filepath := path.Join(ds.Info.Localfs.Path, dd.RelativeUri) + + file, err := os.OpenFile(filepath, os.O_CREATE|os.O_RDWR, 0777) + assert.NoError(t, err) + assert.NotNil(t, file) + + _, err = file.WriteString("hello world!") + assert.NoError(t, err) + assert.NoError(t, file.Close()) + + assert.Error(t, channel.Write(context.Background(), ctx, nil)) + + assert.NoError(t, os.Remove(filepath)) + + // invalidate content-type + ctx.update.ContentType = -1 + assert.Error(t, channel.Write(context.Background(), ctx, nil)) + +} + +func TestLocalFileIOChannel_Write_Success(t *testing.T) { + t.Parallel() + filename := fmt.Sprintf("localtest-%s.txt", uuid.New().String()) + + ctx := initLocalFileDataIOTestRequestContext(t, filename, false) + dd, ds, err := ctx.GetDomainDataAndSource(context.Background()) + assert.NoError(t, err) + assert.NotNil(t, ds) + assert.NotNil(t, dd) + + ctx.update.ContentType = datamesh.ContentType_RAW + + channel := NewBuiltinLocalFileIOChannel() + + inputs := getFlightData(t, [][]byte{}) + + reader, err := flight.NewRecordReader(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: inputs, + }) + assert.NoError(t, err) + assert.NotNil(t, reader) + + // write without + assert.NoError(t, channel.Write(context.Background(), ctx, reader)) + + filepath := path.Join(ds.Info.Localfs.Path, dd.RelativeUri) + + file, err := os.OpenFile(filepath, os.O_CREATE|os.O_RDWR, 0777) + assert.NoError(t, err) + assert.NotNil(t, file) + assert.NoError(t, file.Close()) + + assert.NoError(t, os.Remove(filepath)) +} + +func TestLocalfileIOChannel_Endpoint(t *testing.T) { + t.Parallel() + channel := NewBuiltinLocalFileIOChannel() + assert.Equal(t, BuiltinFlightServerEndpointURI, channel.GetEndpointURI()) +} diff --git a/pkg/datamesh/dmflight/builtin_dataio_oss.go b/pkg/datamesh/dmflight/builtin_dataio_oss.go new file mode 100644 index 00000000..d8cd972b --- /dev/null +++ b/pkg/datamesh/dmflight/builtin_dataio_oss.go @@ -0,0 +1,152 @@ +// Copyright 2023 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "path" + "strings" + + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/pkg/errors" + + "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +const minio string = "minio" + +// BuiltinOssIO defined the oss read & write methond +type BuiltinOssIO struct { + batchReadSize int +} + +func NewBuiltinOssIOChannel() DataMeshDataIOInterface { + return &BuiltinOssIO{ + batchReadSize: 4096, + } +} + +// new oss client +func (o *BuiltinOssIO) newOssSession(config *datamesh.OssDataSourceInfo) (*s3.S3, error) { + nlog.Debugf("Open oss remote endpoint(%s), bucket(%s)", config.Endpoint, config.Bucket) + pathStyle := aws.Bool(false) + region := "" + if strings.ToLower(config.StorageType) == minio { + pathStyle = aws.Bool(true) + region = minio + } else { + region, _ = ParseRegionFromEndpoint(config.Endpoint) + } + + sess, err := session.NewSession(&aws.Config{ + Credentials: credentials.NewStaticCredentials(config.AccessKeyId, config.AccessKeySecret, ""), + Endpoint: &config.Endpoint, + Region: ®ion, + S3ForcePathStyle: pathStyle, + }) + + if err != nil { + nlog.Warnf("Oss(%s) create session failed with error: %s", config.Endpoint, err.Error()) + return nil, err + } + + return s3.New(sess), nil +} + +// DataFlow: RemoteStorage(FileSystem/OSS/...) --> DataProxy --> Client +func (o *BuiltinOssIO) Read(ctx context.Context, rc *DataMeshRequestContext, w *flight.Writer) error { + // init bucket & object stream + dd, ds, err := rc.GetDomainDataAndSource(ctx) + if err != nil { + return err + } + + client, err := o.newOssSession(ds.Info.Oss) + if err != nil { + nlog.Errorf("Create oss client error: %s", err.Error()) + return err + } + obj, err := client.GetObject(&s3.GetObjectInput{ + Bucket: aws.String(ds.Info.Oss.Bucket), + Key: aws.String(path.Join(ds.Info.Oss.Prefix, dd.RelativeUri))}) + if err != nil { + nlog.Error("Oss client get object error: ", err) + return err + } + defer obj.Body.Close() + + switch rc.GetTransferContentType() { + case datamesh.ContentType_RAW: + return DataProxyContentToFlightStreamBinary(dd, obj.Body, w, o.batchReadSize) + case datamesh.ContentType_CSV, datamesh.ContentType_Table: + return DataProxyContentToFlightStreamCSV(dd, obj.Body, w) + default: + return errors.Errorf("invalidate content-type: %s", rc.GetTransferContentType().String()) + } +} + +// DataFlow: Client --> DataProxy --> RemoteStorage(FileSystem/OSS/...) +func (o *BuiltinOssIO) Write(ctx context.Context, rc *DataMeshRequestContext, reader *flight.Reader) error { + dd, ds, err := rc.GetDomainDataAndSource(ctx) + + if err != nil { + return err + } + + objectKey := path.Join(ds.Info.Oss.Prefix, dd.RelativeUri) + nlog.Infof("DomainData(%s) try save to remote oss(%s/%s)", dd.DomaindataId, ds.Info.Oss.Endpoint, + path.Join(ds.Info.Oss.Bucket, objectKey)) + + // init oss client & object stream + client, err := o.newOssSession(ds.Info.Oss) + if err != nil { + nlog.Errorf("Create oss client error: %s", err.Error()) + return err + } + + /*if ok, err := client.IsObjectExist(objectKey); err == nil && ok { + nlog.Warnf("Oss remote file exists, can't upload %s", objectKey) + return status.Errorf(codes.AlreadyExists, "oss remote file exists, can't upload %s", objectKey) + }*/ + + exchanger := NewOSSUploader(ctx, client, ds.Info.Oss.Bucket, objectKey, 5*1024*1024) + defer exchanger.Close() + + switch rc.GetTransferContentType() { + case datamesh.ContentType_RAW: + err = FlightStreamToDataProxyContentBinary(dd, exchanger, reader) + case datamesh.ContentType_CSV, datamesh.ContentType_Table: + err = FlightStreamToDataProxyContentCSV(dd, exchanger, reader) + default: + return errors.Errorf("invalidate content-type: %s", rc.GetTransferContentType().String()) + } + + if err == nil { // no error, close the writer + if err = exchanger.FinishUpload(); err != nil { + nlog.Warnf("Upload to oss failed with %s", err.Error()) + } + } + + return err +} + +func (o *BuiltinOssIO) GetEndpointURI() string { + return BuiltinFlightServerEndpointURI +} diff --git a/pkg/datamesh/dmflight/builtin_dataio_oss_test.go b/pkg/datamesh/dmflight/builtin_dataio_oss_test.go new file mode 100644 index 00000000..9a0980e5 --- /dev/null +++ b/pkg/datamesh/dmflight/builtin_dataio_oss_test.go @@ -0,0 +1,274 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http/httptest" + "testing" + + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/apache/arrow/go/v13/arrow/ipc" + "github.com/google/uuid" + "github.com/johannesboyne/gofakes3" + "github.com/johannesboyne/gofakes3/backend/s3mem" + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/reflect/protoreflect" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" + "github.com/secretflow/kuscia/pkg/datamesh/config" + "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/pkg/utils/tls" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +func registOssDomainDataSource(t *testing.T, conf *config.DataMeshConfig, dsID string) { + lfs, err := json.Marshal(&datamesh.DataSourceInfo{ + Oss: &datamesh.OssDataSourceInfo{ + Endpoint: "", + Bucket: "test", + }}) + assert.NoError(t, err) + + strConfig, err := tls.EncryptOAEP(&conf.DomainKey.PublicKey, lfs) + assert.NoError(t, err) + + _, err = conf.KusciaClient.KusciaV1alpha1().DomainDataSources(conf.KubeNamespace).Create(context.Background(), &v1alpha1.DomainDataSource{ + ObjectMeta: v1.ObjectMeta{ + Name: dsID, + }, + Spec: v1alpha1.DomainDataSourceSpec{ + Name: dsID, + Type: "oss", + Data: map[string]string{ + "encryptedInfo": strConfig, + }, + }, + }, v1.CreateOptions{}) + + assert.NoError(t, err) +} + +func initOssDataIOTestRequestContext(t *testing.T, filename string, isQuery bool) (*config.DataMeshConfig, *DataMeshRequestContext) { + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + // init ok + registOssDomainDataSource(t, conf, "oss-data-source") + domainDataID := registDomainData(t, conf, "oss-data-source", filename) + var msg protoreflect.ProtoMessage + if isQuery { + msg = &datamesh.CommandDomainDataQuery{ + DomaindataId: domainDataID, + } + } else { + msg = &datamesh.CommandDomainDataUpdate{ + DomaindataId: domainDataID, + } + } + ctx, err := NewDataMeshRequestContext(domainDataService, datasourceService, msg) + + assert.NoError(t, err) + assert.NotNil(t, ctx) + return conf, ctx +} + +func updateOssDataSourceConfig(t *testing.T, conf *config.DataMeshConfig, ctx *DataMeshRequestContext) (*s3mem.Backend, *httptest.Server) { + // oss file not exists + backend := s3mem.New() + faker := gofakes3.New(backend) + ts := httptest.NewServer(faker.Server()) + + // change datasource config + ds, err := conf.KusciaClient.KusciaV1alpha1().DomainDataSources(conf.KubeNamespace).Get(context.Background(), "oss-data-source", v1.GetOptions{}) + assert.NoError(t, err) + assert.NotNil(t, ds) + assert.NotNil(t, ds.Spec) + + lfs, err := json.Marshal(&datamesh.DataSourceInfo{ + Oss: &datamesh.OssDataSourceInfo{ + Endpoint: ts.URL, + Bucket: "test", + Prefix: "", + StorageType: "minio", + AccessKeyId: "xz", + AccessKeySecret: "mmmm", + }}) + assert.NoError(t, err) + + strConfig, err := tls.EncryptOAEP(&conf.DomainKey.PublicKey, lfs) + assert.NoError(t, err) + + ds.Spec.Data = map[string]string{ + "encryptedInfo": strConfig, + } + _, err = conf.KusciaClient.KusciaV1alpha1().DomainDataSources(conf.KubeNamespace).Update(context.Background(), ds, v1.UpdateOptions{}) + assert.NoError(t, err) + + return backend, ts +} + +func TestNewBuiltinOssIOChannel(t *testing.T) { + t.Parallel() + assert.NotNil(t, NewBuiltinOssIOChannel()) +} + +func TestOssIOChannel_Read_DomainData_Invalidate(t *testing.T) { + t.Parallel() + channel := NewBuiltinOssIOChannel() + + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + ctx, err := NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataQuery{ + DomaindataId: "not-exists-domain-data", + ContentType: datamesh.ContentType_RAW, + }) + + assert.NoError(t, err) + assert.NotNil(t, ctx) + + assert.Error(t, channel.Read(context.Background(), ctx, nil)) +} + +func TestOssIOChannel_Read_OssQueryFailed(t *testing.T) { + t.Parallel() + filename := fmt.Sprintf("osstest-%s.txt", uuid.New().String()) + conf, ctx := initOssDataIOTestRequestContext(t, filename, true) + + channel := NewBuiltinOssIOChannel() + // create oss session failed + assert.Error(t, channel.Read(context.Background(), ctx, nil)) + + _, ts := updateOssDataSourceConfig(t, conf, ctx) + defer ts.Close() + + // oss file not exists + assert.Error(t, channel.Read(context.Background(), ctx, nil)) +} + +func TestOssIOChannel_Read_Success(t *testing.T) { + t.Parallel() + filename := fmt.Sprintf("osstest-%s.txt", uuid.New().String()) + conf, ctx := initOssDataIOTestRequestContext(t, filename, true) + + channel := NewBuiltinOssIOChannel() + // create oss session failed + assert.Error(t, channel.Read(context.Background(), ctx, nil)) + + backend, ts := updateOssDataSourceConfig(t, conf, ctx) + defer ts.Close() + + assert.NoError(t, backend.CreateBucket("test")) + _, err := backend.PutObject("test", filename, map[string]string{}, bytes.NewReader([]byte("abcdefg")), 7) + assert.NoError(t, err) + + // invalidate type + ctx.query.ContentType = -1 + assert.Error(t, channel.Read(context.Background(), ctx, nil)) + + ctx.query.ContentType = datamesh.ContentType_RAW + + mgs := &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + } + writer := flight.NewRecordWriter(mgs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + assert.NotNil(t, writer) + + // write success + assert.NoError(t, channel.Read(context.Background(), ctx, writer)) + assert.NotEmpty(t, mgs.dataList) +} + +func TestOssIOChannel_Write_DomainData_Invalidate(t *testing.T) { + t.Parallel() + channel := NewBuiltinOssIOChannel() + + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + ctx, err := NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataQuery{ + DomaindataId: "not-exists-domain-data", + ContentType: datamesh.ContentType_RAW, + }) + + assert.NoError(t, err) + assert.NotNil(t, ctx) + + assert.Error(t, channel.Write(context.Background(), ctx, nil)) +} + +func TestOssIOChannel_Write_RemoteOSSFailed(t *testing.T) { + t.Parallel() + + filename := fmt.Sprintf("osstest-%s.txt", uuid.New().String()) + conf, ctx := initOssDataIOTestRequestContext(t, filename, false) + + channel := NewBuiltinOssIOChannel() + + _, ts := updateOssDataSourceConfig(t, conf, ctx) + defer ts.Close() + + // invalidate content-type + ctx.update.ContentType = -1 + assert.Error(t, channel.Write(context.Background(), ctx, nil)) +} + +func TestOssIOChannel_Write_Success(t *testing.T) { + t.Parallel() + filename := fmt.Sprintf("osstest-%s.txt", uuid.New().String()) + conf, ctx := initOssDataIOTestRequestContext(t, filename, false) + + channel := NewBuiltinOssIOChannel() + + _, ts := updateOssDataSourceConfig(t, conf, ctx) + defer ts.Close() + + ctx.update.ContentType = datamesh.ContentType_RAW + + inputs := getFlightData(t, [][]byte{}) + + reader, err := flight.NewRecordReader(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: inputs, + }) + assert.NoError(t, err) + assert.NotNil(t, reader) + + // write without + assert.NoError(t, channel.Write(context.Background(), ctx, reader)) +} + +func TestOssIOChannel_Endpoint(t *testing.T) { + t.Parallel() + channel := NewBuiltinOssIOChannel() + assert.Equal(t, BuiltinFlightServerEndpointURI, channel.GetEndpointURI()) +} diff --git a/pkg/datamesh/dmflight/context.go b/pkg/datamesh/dmflight/context.go new file mode 100644 index 00000000..97cf6a52 --- /dev/null +++ b/pkg/datamesh/dmflight/context.go @@ -0,0 +1,129 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/proto/api/v1alpha1" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +type DataMeshRequestContext struct { + io DataMeshDataIOInterface + dsType string + query *datamesh.CommandDomainDataQuery + update *datamesh.CommandDomainDataUpdate + + domainDataService service.IDomainDataService + domainDataSourceService service.IDomainDataSourceService +} + +func NewDataMeshRequestContext(dd service.IDomainDataService, ds service.IDomainDataSourceService, msg proto.Message) (*DataMeshRequestContext, error) { + info := &DataMeshRequestContext{ + domainDataService: dd, + domainDataSourceService: ds, + } + + switch msg := msg.(type) { + case *datamesh.CommandDomainDataQuery: + info.query = msg + case *datamesh.CommandDomainDataUpdate: + info.update = msg + default: + return nil, status.Error(codes.InvalidArgument, "FlightDescriptor.Cmd of GetFlightInfo Request is invalid, need CommandDomainDataQuery/CommandDomainDataUpdate") + } + + return info, nil +} + +func (rc *DataMeshRequestContext) GetDataSourceType() (string, error) { + if rc.dsType == "" { + dds, err := rc.GetDomainDataSource(context.Background()) + if err != nil { + return "", status.Error(codes.InvalidArgument, err.Error()) + } + rc.dsType = dds.GetType() + } + + return rc.dsType, nil +} + +func (rc *DataMeshRequestContext) GetDomainData(ctx context.Context) (*datamesh.DomainData, error) { + domainDataReq := &datamesh.QueryDomainDataRequest{ + DomaindataId: rc.getDomainDataID(), + } + + domainDataResp := rc.domainDataService.QueryDomainData(ctx, domainDataReq) + if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { + var appStatus *v1alpha1.Status + if domainDataResp.GetStatus() != nil { + appStatus = domainDataResp.GetStatus() + } + return nil, common.BuildGrpcErrorf(appStatus, codes.Internal, "query domain data by id(%s) fail", rc.getDomainDataID()) + } + return domainDataResp.Data, nil +} + +func (rc *DataMeshRequestContext) GetDomainDataAndSource(ctx context.Context) (*datamesh.DomainData, *datamesh.DomainDataSource, error) { + var data *datamesh.DomainData + var err error + if data, err = rc.GetDomainData(ctx); err != nil { + return nil, nil, err + } + + datasourceReq := &datamesh.QueryDomainDataSourceRequest{ + DatasourceId: data.DatasourceId, + } + + datasourceResp := rc.domainDataSourceService.QueryDomainDataSource(ctx, datasourceReq) + if datasourceResp == nil || datasourceResp.GetStatus() == nil || datasourceResp.GetStatus().GetCode() != 0 { + var appStatus *v1alpha1.Status + if datasourceResp.GetStatus() != nil { + appStatus = datasourceResp.GetStatus() + } + return data, nil, common.BuildGrpcErrorf(appStatus, codes.FailedPrecondition, "query data source by id(%s) fail", datasourceReq.DatasourceId) + } + return data, datasourceResp.GetData(), nil +} + +func (rc *DataMeshRequestContext) GetDomainDataSource(ctx context.Context) (*datamesh.DomainDataSource, error) { + _, ds, err := rc.GetDomainDataAndSource(ctx) + return ds, err +} + +func (rc *DataMeshRequestContext) getDomainDataID() string { + if rc.query != nil { + return rc.query.DomaindataId + } + + return rc.update.DomaindataId +} + +func (rc *DataMeshRequestContext) GetTransferContentType() datamesh.ContentType { + if rc.query != nil { + return rc.query.ContentType + } else if rc.update != nil { + return rc.update.ContentType + } + + return datamesh.ContentType_RAW +} diff --git a/pkg/datamesh/dmflight/context_test.go b/pkg/datamesh/dmflight/context_test.go new file mode 100644 index 00000000..39955510 --- /dev/null +++ b/pkg/datamesh/dmflight/context_test.go @@ -0,0 +1,198 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "encoding/json" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" + kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" + "github.com/secretflow/kuscia/pkg/datamesh/config" + "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/pkg/utils/tls" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +func initContextTestEnv(t *testing.T) *config.DataMeshConfig { + conf := config.NewDefaultDataMeshConfig() + + assert.NotNil(t, conf) + conf.KusciaClient = kusciafake.NewSimpleClientset() + conf.KubeNamespace = "test-namespace" + + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + assert.NoError(t, err) + conf.DomainKey = privateKey + + return conf +} + +func registDomainData(t *testing.T, conf *config.DataMeshConfig, dsID, pathName string) string { + domainDataID := "data-" + uuid.New().String() + _, err := conf.KusciaClient.KusciaV1alpha1().DomainDatas(conf.KubeNamespace).Create(context.Background(), &v1alpha1.DomainData{ + ObjectMeta: v1.ObjectMeta{ + Name: domainDataID, + }, + Spec: v1alpha1.DomainDataSpec{ + RelativeURI: pathName, + Name: domainDataID, + Type: "RAW", + DataSource: dsID, + Author: conf.KubeNamespace, + }, + }, v1.CreateOptions{}) + assert.NoError(t, err) + + return domainDataID +} + +func registLocalFileDomainDataSource(t *testing.T, conf *config.DataMeshConfig, dsID string) { + lfs, err := json.Marshal(&datamesh.DataSourceInfo{ + Localfs: &datamesh.LocalDataSourceInfo{ + Path: "/tmp/var", + }}) + assert.NoError(t, err) + + strConfig, err := tls.EncryptOAEP(&conf.DomainKey.PublicKey, lfs) + assert.NoError(t, err) + + _, err = conf.KusciaClient.KusciaV1alpha1().DomainDataSources(conf.KubeNamespace).Create(context.Background(), &v1alpha1.DomainDataSource{ + ObjectMeta: v1.ObjectMeta{ + Name: dsID, + }, + Spec: v1alpha1.DomainDataSourceSpec{ + Name: dsID, + Type: "localfs", + Data: map[string]string{ + "encryptedInfo": strConfig, + }, + }, + }, v1.CreateOptions{}) + + assert.NoError(t, err) +} + +func TestNewDataMeshRequestContext(t *testing.T) { + t.Parallel() + ctx, err := NewDataMeshRequestContext(nil, nil, nil) + assert.Error(t, err) + assert.Nil(t, ctx) + + conf := initContextTestEnv(t) + + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + // query + ctx, err = NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataQuery{ + DomaindataId: "test-data", + }) + assert.NoError(t, err) + assert.NotNil(t, ctx) + assert.Nil(t, ctx.io) + assert.Nil(t, ctx.update) + assert.NotNil(t, ctx.query) + assert.Equal(t, "", ctx.dsType) + + // update + // query + ctx, err = NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataUpdate{ + DomaindataId: "test-data", + }) + assert.NoError(t, err) + assert.NotNil(t, ctx) + assert.Nil(t, ctx.io) + assert.NotNil(t, ctx.update) + assert.Nil(t, ctx.query) + assert.Equal(t, "", ctx.dsType) +} + +func TestGetDataSourceType(t *testing.T) { + t.Parallel() + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + // domain-data not exists + ctx, err := NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataQuery{ + DomaindataId: "test-data", + }) + assert.NoError(t, err) + assert.NotNil(t, ctx) + + dsType, err := ctx.GetDataSourceType() + assert.Error(t, err) + assert.Equal(t, "", dsType) + + // init ok + domainDataID := registDomainData(t, conf, common.DefaultDataSourceID, "localtest.txt") + ctx, err = NewDataMeshRequestContext(domainDataService, datasourceService, &datamesh.CommandDomainDataQuery{ + DomaindataId: domainDataID, + }) + + assert.NoError(t, err) + assert.NotNil(t, ctx) + + // datasource not exists + dsType, err = ctx.GetDataSourceType() + assert.Error(t, err) + assert.Equal(t, "", dsType) + + registLocalFileDomainDataSource(t, conf, common.DefaultDataSourceID) + dsType, err = ctx.GetDataSourceType() + assert.NoError(t, err) + assert.Equal(t, "localfs", dsType) + assert.Equal(t, "localfs", ctx.dsType) +} + +func TestGetTransferType(t *testing.T) { + t.Parallel() + // query + ctx, err := NewDataMeshRequestContext(nil, nil, &datamesh.CommandDomainDataQuery{ + DomaindataId: "test-data", + ContentType: datamesh.ContentType_RAW, + }) + assert.NoError(t, err) + assert.NotNil(t, ctx) + + assert.Equal(t, "test-data", ctx.getDomainDataID()) + assert.Equal(t, datamesh.ContentType_RAW, ctx.GetTransferContentType()) + + // update + ctx, err = NewDataMeshRequestContext(nil, nil, &datamesh.CommandDomainDataUpdate{ + DomaindataId: "test-data", + ContentType: datamesh.ContentType_RAW, + }) + assert.NoError(t, err) + assert.NotNil(t, ctx) + + assert.Equal(t, "test-data", ctx.getDomainDataID()) + assert.Equal(t, datamesh.ContentType_RAW, ctx.GetTransferContentType()) +} diff --git a/pkg/datamesh/dmflight/dataio.go b/pkg/datamesh/dmflight/dataio.go new file mode 100644 index 00000000..75e29b06 --- /dev/null +++ b/pkg/datamesh/dmflight/dataio.go @@ -0,0 +1,150 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "io" + "strings" + + "github.com/apache/arrow/go/v13/arrow" + "github.com/apache/arrow/go/v13/arrow/array" + "github.com/apache/arrow/go/v13/arrow/csv" + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/apache/arrow/go/v13/arrow/memory" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +type DataMeshDataIOInterface interface { + Read(ctx context.Context, rc *DataMeshRequestContext, w *flight.Writer) error + + Write(ctx context.Context, rc *DataMeshRequestContext, stream *flight.Reader) error + + GetEndpointURI() string +} + +// DataFlow(Table): RemoteStorage(FileSystem/OSS/...) --> DataProxy --> Client +func DataProxyContentToFlightStreamCSV(data *datamesh.DomainData, r io.Reader, w *flight.Writer) error { + //generate arrow schema + schema, err := GenerateArrowSchema(data) + if err != nil { + nlog.Errorf("Domaindata(%s) generate arrow schema error: %s", data.GetDomaindataId(), err.Error()) + return status.Errorf(codes.Internal, "generate arrow schema failed with %s", err.Error()) + } + + // use csv reader + csvReader := csv.NewReader(r, schema) + + for csvReader.Next() { + record := csvReader.Record() + record.Retain() + if err := w.Write(record); err != nil { + nlog.Warnf("Domaindata(%s) to flight stream failed with error %s", data.DomaindataId, err.Error()) + return err + } + nlog.Debugf("Domaindata(%s) send rows=%d", data.DomaindataId, record.NumRows()) + } + + defer csvReader.Release() + + nlog.Infof("Domaindata(%s), file(%s) finish read and send", data.DomaindataId, data.RelativeUri) + + return nil +} + +// DataFlow(Binary): RemoteStorage(FileSystem/OSS/...) --> DataProxy --> Client +func DataProxyContentToFlightStreamBinary(data *datamesh.DomainData, r io.Reader, w *flight.Writer, batchReadSize int) error { + buffer := make([]byte, batchReadSize) + hasMoreContent := true + for hasMoreContent { + bytesRead, err := r.Read(buffer) + if err != nil { + if err == io.EOF { + if bytesRead <= 0 { + break + } + hasMoreContent = false + } else { + return err + } + } + + builder := array.NewBinaryBuilder(memory.NewGoAllocator(), arrow.BinaryTypes.Binary) + builder.Append(buffer[:bytesRead]) + + c := array.NewRecord(GenerateBinaryDataArrowSchema(), []arrow.Array{builder.NewArray()}, 1) + if err := w.Write(c); err != nil { + nlog.Warnf("Domaindata(%s), file(%s) write batch failed with error:%s", data.DomaindataId, data.RelativeUri, err.Error()) + return err + } + } + + nlog.Infof("Domaindata(%s), file(%s) finish read and sand", data.DomaindataId, data.RelativeUri) + + return nil +} + +// DataFlow(Table): Client --> DataProxy --> RemoteStorage(FileSystem/OSS/...) +func FlightStreamToDataProxyContentCSV(data *datamesh.DomainData, w io.Writer, reader *flight.Reader) error { + for reader.Next() { + record := reader.Record() + record.Retain() + columns := record.Columns() + strColumns := make([]string, 0) + for index := 0; index < len(columns); index++ { + strColumns = append(strColumns, columns[index].ValueStr(0)) + } + + if _, err := w.Write([]byte(strings.Join(strColumns, ","))); err != nil { + nlog.Warnf("Domaindata(%s) write content to remote failed with error: %s", data.GetDomaindataId(), err.Error()) + return err + } + } + return nil +} + +// DataFlow(Binary): Client --> DataProxy --> RemoteStorage(FileSystem/OSS/...) +func FlightStreamToDataProxyContentBinary(data *datamesh.DomainData, w io.Writer, reader *flight.Reader) error { + for reader.Next() { + cnt := reader.Record() + cnt.Retain() + defer cnt.Release() + + for idx, col := range cnt.Columns() { + if !arrow.IsBaseBinary(col.DataType().ID()) { + nlog.Warnf("Input data column(%d) is not binary, type=%s", idx, col.DataType().String()) + return status.Errorf(codes.InvalidArgument, "domaindata(%s) input data is not binary. type=%s", data.GetDomaindataId(), col.DataType().String()) + } + + if b, ok := col.(*array.Binary); ok { + for i := 0; i < b.Len(); i++ { + data := b.Value(i) + if _, err := w.Write(data); err != nil { + nlog.Warnf("Write buf length=%d, failed with err: %s", len(data), err.Error()) + return err + } + } + } + + } + + } + + return nil +} diff --git a/pkg/datamesh/dmflight/dataio_test.go b/pkg/datamesh/dmflight/dataio_test.go new file mode 100644 index 00000000..6da2289a --- /dev/null +++ b/pkg/datamesh/dmflight/dataio_test.go @@ -0,0 +1,206 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "bytes" + "errors" + "testing" + + "github.com/apache/arrow/go/v13/arrow" + "github.com/apache/arrow/go/v13/arrow/array" + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/apache/arrow/go/v13/arrow/ipc" + "github.com/apache/arrow/go/v13/arrow/memory" + "github.com/stretchr/testify/assert" + gomonkeyv2 "github.com/agiledragon/gomonkey/v2" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +func getTestDomainData() *datamesh.DomainData { + return &datamesh.DomainData{ + DomaindataId: "test-domain-data", + DatasourceId: common.DefaultDataSourceID, + } +} + +func TestDataProxyContentToFlightStreamBinary_WriteFailed(t *testing.T) { + data := getTestDomainData() + + reader := bytes.NewReader([]byte("abcdefg")) + + writer := &flight.Writer{} + // mock writer.write + + patch := gomonkeyv2.ApplyMethod(writer, "Write", func(w *flight.Writer, rec arrow.Record) error { + return errors.New("write failed") + }) + defer patch.Reset() + + assert.Error(t, DataProxyContentToFlightStreamBinary(data, reader, writer, 1024)) +} + +func TestDataProxyContentToFlightStreamBinary_WriteSuccess(t *testing.T) { + t.Parallel() + data := getTestDomainData() + + reader := bytes.NewReader([]byte("abcdefg")) + + mgs := &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + } + + writer := flight.NewRecordWriter(mgs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + + assert.NoError(t, DataProxyContentToFlightStreamBinary(data, reader, writer, 1024)) + + assert.Len(t, mgs.dataList, 2) + + res, err := flight.NewRecordReader(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: mgs.dataList, + }) + assert.NoError(t, err) + assert.NotNil(t, res) + assert.True(t, res.Next()) + + rec := res.Record() + assert.NotNil(t, rec) + + assert.NotNil(t, rec) + assert.Equal(t, int64(1), rec.NumCols()) + assert.Equal(t, "data", rec.ColumnName(0)) + + col := rec.Columns()[0] + assert.NotNil(t, col) + + assert.Equal(t, arrow.BinaryTypes.Binary, col.DataType()) + + b, ok := col.(*array.Binary) + assert.True(t, ok) + assert.NotNil(t, b) + assert.Equal(t, 1, b.Len()) + assert.Equal(t, "abcdefg", string(b.Value(0))) +} + +func TestDataProxyContentToFlightStreamBinary_Loop(t *testing.T) { + t.Parallel() + data := getTestDomainData() + + reader := bytes.NewReader(make([]byte, 100)) + + mgs := &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + } + + writer := flight.NewRecordWriter(mgs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + + assert.NoError(t, DataProxyContentToFlightStreamBinary(data, reader, writer, 50)) + // first data is schema + assert.Equal(t, 3, len(mgs.dataList)) +} + +func getFlightData(t *testing.T, inputs [][]byte) []*flight.FlightData { + mgs := &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + } + + writer := flight.NewRecordWriter(mgs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + + for i := 0; i < len(inputs); i++ { + + builder := array.NewBinaryBuilder(memory.NewGoAllocator(), arrow.BinaryTypes.Binary) + builder.Append(inputs[i]) + + assert.NoError(t, writer.Write(array.NewRecord(GenerateBinaryDataArrowSchema(), []arrow.Array{builder.NewArray()}, 1))) + } + + writer.Close() + + return mgs.dataList +} + +func TestFlightStreamToDataProxyContentBinary_NoData(t *testing.T) { + t.Parallel() + data := getTestDomainData() + + inputs := getFlightData(t, [][]byte{}) + + reader, err := flight.NewRecordReader(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: inputs, + }) + assert.NoError(t, err) + + writer := bytes.NewBuffer([]byte{}) + + assert.NoError(t, FlightStreamToDataProxyContentBinary(data, writer, reader)) +} + +func TestFlightStreamToDataProxyContentBinary_Success(t *testing.T) { + t.Parallel() + data := getTestDomainData() + + writer := bytes.NewBuffer([]byte{}) + + inputs := getFlightData(t, [][]byte{[]byte("xyz")}) + + reader, err := flight.NewRecordReader(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: inputs, + }) + assert.NoError(t, err) + + assert.NoError(t, FlightStreamToDataProxyContentBinary(data, writer, reader)) + assert.Equal(t, "xyz", writer.String()) +} + +func TestFlightStreamToDataProxyContentBinary_ErrorFormat(t *testing.T) { + data := getTestDomainData() + + writer := bytes.NewBuffer([]byte{}) + + reader := &flight.Reader{} + + times := 0 + patch := gomonkeyv2.ApplyMethod(reader.Reader, "Next", func(*ipc.Reader) bool { + times++ + // first return true, second return false + return times == 1 + }) + + patch.ApplyMethod(reader.Reader, "Record", func(*ipc.Reader) arrow.Record { + // column error + builder := array.NewBinaryBuilder(memory.NewGoAllocator(), arrow.BinaryTypes.Binary) + builder.Append([]byte("xyz")) + schema, _ := GenerateBinaryDataArrowSchema().AddField(1, arrow.Field{ + Name: "invalidate", + Type: &arrow.BooleanType{}, + Nullable: false, + }) + + builder2 := array.NewBooleanBuilder(memory.NewGoAllocator()) + builder2.Append(true) + + return array.NewRecord(schema, []arrow.Array{builder.NewArray(), builder2.NewArray()}, 1) + + }) + defer patch.Reset() + + assert.Error(t, FlightStreamToDataProxyContentBinary(data, writer, reader)) + assert.Equal(t, "xyz", writer.String()) +} diff --git a/pkg/datamesh/dmflight/datamesh_handler.go b/pkg/datamesh/dmflight/datamesh_handler.go new file mode 100644 index 00000000..29a7456f --- /dev/null +++ b/pkg/datamesh/dmflight/datamesh_handler.go @@ -0,0 +1,284 @@ +// Copyright 2023 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/apache/arrow/go/v13/arrow" + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/apache/arrow/go/v13/arrow/ipc" + "github.com/apache/arrow/go/v13/arrow/memory" + "github.com/google/uuid" + cache "github.com/patrickmn/go-cache" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/datamesh/config" + "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +type CustomActionHandler func(context.Context, []byte) (*flight.Result, error) + +type datameshFlightHandler struct { + flight.BaseFlightServer + cmds *cache.Cache + + iochannels map[string]DataMeshDataIOInterface + + customHandles map[string]CustomActionHandler + + domainDataService service.IDomainDataService + domainDataSourceService service.IDomainDataSourceService +} + +func NewDataMeshFlightHandler(dds service.IDomainDataService, dss service.IDomainDataSourceService, configs []config.ExternalDataProxyConfig) flight.FlightServer { + handler := &datameshFlightHandler{ + cmds: cache.New(time.Minute*10, time.Minute), + customHandles: map[string]CustomActionHandler{}, + iochannels: map[string]DataMeshDataIOInterface{ + "localfs": NewBuiltinLocalFileIOChannel(), + "oss": NewBuiltinOssIOChannel(), + }, + domainDataService: dds, + domainDataSourceService: dss, + } + + chs := NewDataMeshCustomActionHandlers(dds, dss) + handler.customHandles["ActionCreateDomainDataRequest"] = chs.DoActionCreateDomainDataRequest + handler.customHandles["ActionQueryDomainDataRequest"] = chs.DoActionQueryDomainDataRequest + handler.customHandles["ActionUpdateDomainDataRequest"] = chs.DoActionUpdateDomainDataRequest + handler.customHandles["ActionDeleteDomainDataRequest"] = chs.DoActionDeleteDomainDataRequest + handler.customHandles["ActionQueryDomainDataSourceRequest"] = chs.DoActionQueryDomainDataSourceRequest + + for _, cfg := range configs { + for _, dstype := range cfg.DataSourceTypes { + nlog.Infof("Extenal dataproxy type[%s] mode(%s) %s", dstype, cfg.Mode, cfg.Endpoint) + + if strings.ToLower(cfg.Mode) == string(config.ModeDirect) { + handler.iochannels[dstype] = NewBuiltinDirectIO(&cfg) + } + } + } + return handler +} + +func (f *datameshFlightHandler) GetSchema(ctx context.Context, request *flight.FlightDescriptor) (*flight.SchemaResult, error) { + var ( + anyCmd anypb.Any + msg proto.Message + err error + ) + + if err = proto.Unmarshal(request.Cmd, &anyCmd); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "unable to parse command: %s", err.Error()) + } + + if msg, err = anyCmd.UnmarshalNew(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "could not unmarshal Any to a command type: %s", err.Error()) + } + + switch cmd := msg.(type) { + case *datamesh.CommandGetDomainDataSchema: + res := f.domainDataService.QueryDomainData(ctx, &datamesh.QueryDomainDataRequest{ + DomaindataId: cmd.DomaindataId, + }) + if res.GetStatus() != nil && res.GetStatus().GetCode() != 0 { + return nil, common.BuildGrpcErrorf(res.GetStatus(), codes.Internal, "query domain data by id(%s) fail", cmd.DomaindataId) + } + + domainData := res.Data + + var fields []arrow.Field + for _, column := range domainData.Columns { + colType := common.Convert2ArrowColumnType(column.Type) + if colType == nil { + return nil, common.BuildGrpcErrorf(nil, codes.FailedPrecondition, "invalid column(%s) with type(%s)", column.Name, + column.Type) + } + fields = append(fields, arrow.Field{ + Name: column.Name, + Type: colType, + Nullable: !column.NotNullable, + }) + } + + metadata := arrow.NewMetadata([]string{"type"}, []string{domainData.Type}) + return &flight.SchemaResult{ + Schema: flight.SerializeSchema(arrow.NewSchema(fields, &metadata), memory.DefaultAllocator), + }, nil + } + + return nil, status.Error(codes.InvalidArgument, "request command is invalid") +} + +func (f *datameshFlightHandler) GetFlightInfo(ctx context.Context, request *flight.FlightDescriptor) (fi *flight.FlightInfo, ret error) { + var ( + anyCmd anypb.Any + info *DataMeshRequestContext + ) + + defer func() { + if r := recover(); r != nil { + nlog.Warnf("[DataMesh] [GetFlightInfo] recover with error %v", r) + ret = status.Error(codes.Internal, fmt.Sprintf("unknown error, msg=%v", r)) + } + }() + + if err := proto.Unmarshal(request.Cmd, &anyCmd); err != nil { + nlog.Warnf("Unable to parse FlightDescriptor.Cmd to Any") + return nil, status.Errorf(codes.InvalidArgument, "unable to parse command: %v", err) + } + + msg, err := anyCmd.UnmarshalNew() + if err != nil { + nlog.Warnf("FlightDescriptor.Cmd UnmarshalNew fail: %s", err.Error()) + return nil, status.Errorf(codes.InvalidArgument, "could not unmarshal Any to a command type: %s", err.Error()) + } + + if info, err = NewDataMeshRequestContext(f.domainDataService, f.domainDataSourceService, msg); err != nil { + nlog.Warnf("GetFlightInfo create context fail: %s", err.Error()) + return nil, err + } + + tickUUID := uuid.New().String() + + dataSource, err := info.GetDomainDataSource(context.Background()) + if err != nil { + return nil, status.Errorf(codes.Internal, "get datasource type failed with %s", err.Error()) + } + channel, ok := f.iochannels[dataSource.Type] + if !ok { + nlog.Warnf("Not found io channel for datasource type: %s", dataSource.Type) + return nil, status.Errorf(codes.InvalidArgument, "datasource type (%s) not supported", dataSource.Type) + } + + info.io = channel + + if err := f.cmds.Add(string(tickUUID), info, 10*time.Minute); err != nil { + return nil, status.Error(codes.Internal, + fmt.Sprintf("cache domaindata failed, please try call GetFlightInfo again. raw message=(%s)", err.Error())) + } + + nlog.Infof("[DataMesh] [GetFlightInfo] ticket=%s", string(tickUUID)) + + return CreateDateMeshFlightInfo([]byte(tickUUID), channel.GetEndpointURI()), nil +} + +func (f *datameshFlightHandler) DoAction(action *flight.Action, stream flight.FlightService_DoActionServer) (ret error) { + defer func() { + if r := recover(); r != nil { + nlog.Warnf("[DataMesh] [DoAction] recover with error %v", r) + ret = status.Error(codes.Internal, fmt.Sprintf("unknown error, msg=%v", r)) + } + }() + + if cah, ok := f.customHandles[action.Type]; ok { + result, err := cah(context.Background(), action.GetBody()) + if err != nil { + nlog.Warnf("[DataMesh] process action(%s) failed with error: %s", action.GetType(), err.Error()) + return err + } + + return stream.Send(result) + } + + return status.Errorf(codes.InvalidArgument, "unsupported action type: %s", action.Type) +} + +func (f *datameshFlightHandler) DoGet(tkt *flight.Ticket, fs flight.FlightService_DoGetServer) (ret error) { + defer func() { + if r := recover(); r != nil { + nlog.Warnf("[DataMesh] [DoGet] recover with error %v", r) + ret = status.Error(codes.Internal, fmt.Sprintf("unknown error, msg=%v", r)) + } + }() + + ticketID := string(tkt.Ticket) + nlog.Infof("[DataMesh] [DoGet] ticket=%s", ticketID) + channel, ok := f.cmds.Get(ticketID) + if !ok || channel == nil { + nlog.Warnf("[DataMesh] [DoGet] invalidate input ticket=%s", ticketID) + return status.Errorf(codes.InvalidArgument, "invalid ticket:%s", ticketID) + } + + ioInfo := channel.(*DataMeshRequestContext) + + var w *flight.Writer + if ioInfo.GetTransferContentType() == datamesh.ContentType_RAW { + w = flight.NewRecordWriter(fs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + } else { + data, err := ioInfo.GetDomainData(context.Background()) + if err != nil { + return status.Error(codes.Internal, err.Error()) + } + + schema, err := GenerateArrowSchema(data) + if err != nil { + nlog.Errorf("Domaindata(%s) generate arrow schema error: %s", data.GetDomaindataId(), err.Error()) + return status.Errorf(codes.Internal, "generate arrow schema failed with %s", err.Error()) + } + w = flight.NewRecordWriter(fs, ipc.WithSchema(schema)) + } + + defer w.Close() + + return ioInfo.io.Read(fs.Context(), ioInfo, w) + +} + +func (f *datameshFlightHandler) DoPut(stream flight.FlightService_DoPutServer) (ret error) { + defer func() { + if r := recover(); r != nil { + nlog.Warnf("[DataMesh] [DoPut] recover with error %v", r) + ret = status.Error(codes.Internal, fmt.Sprintf("unknown error, msg=%v", r)) + } + }() + + reader, err := flight.NewRecordReader(stream) + if err != nil { + nlog.Warnf("[DataMesh] [DoPut] create record reader failed with %s", err.Error()) + return status.Error(codes.Internal, err.Error()) + } + defer reader.Release() + + desc := reader.LatestFlightDescriptor() + if desc == nil { + nlog.Warnf("[DataMesh] [DoPut] not found Descriptor") + return status.Error(codes.InvalidArgument, "not found Descriptor") + } + + ticketID := string(desc.Cmd) + + nlog.Infof("[DataMesh] [DoPut] ticket=%s", ticketID) + + channel, ok := f.cmds.Get(ticketID) + if !ok || channel == nil { + nlog.Warnf("[DataMesh] [DoPut] invalidate input ticket=%s", ticketID) + return status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid ticket:%s", ticketID)) + } + + ioInfo := channel.(*DataMeshRequestContext) + + return ioInfo.io.Write(stream.Context(), ioInfo, reader) +} diff --git a/pkg/datamesh/dmflight/datamesh_handler_test.go b/pkg/datamesh/dmflight/datamesh_handler_test.go new file mode 100644 index 00000000..3f70c565 --- /dev/null +++ b/pkg/datamesh/dmflight/datamesh_handler_test.go @@ -0,0 +1,458 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "bytes" + "context" + "encoding/gob" + "encoding/json" + "fmt" + "io" + "testing" + "time" + + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/apache/arrow/go/v13/arrow/ipc" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/proto" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" + "github.com/secretflow/kuscia/pkg/datamesh/config" + "github.com/secretflow/kuscia/pkg/datamesh/service" + "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/pkg/utils/tls" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +type mockIOChannel struct { + nextError error +} + +func (mio *mockIOChannel) Read(ctx context.Context, rc *DataMeshRequestContext, w *flight.Writer) error { + return mio.nextError +} + +func (mio *mockIOChannel) Write(ctx context.Context, rc *DataMeshRequestContext, stream *flight.Reader) error { + return mio.nextError +} + +func (mio *mockIOChannel) GetEndpointURI() string { + return "" +} + +type mockGrpcServerStream struct{} + +func (*mockGrpcServerStream) Context() context.Context { + return context.Background() +} +func (*mockGrpcServerStream) RecvMsg(m any) error { return nil } +func (*mockGrpcServerStream) SendHeader(metadata.MD) error { return nil } +func (*mockGrpcServerStream) SendMsg(m any) error { return nil } +func (*mockGrpcServerStream) SetHeader(metadata.MD) error { return nil } +func (*mockGrpcServerStream) SetTrailer(metadata.MD) {} + +type mockDoGetServer struct { + grpc.ServerStream + + dataList []*flight.FlightData +} + +func deepCopy(src *flight.FlightData) *flight.FlightData { + var dest bytes.Buffer + enc := gob.NewEncoder(&dest) + dec := gob.NewDecoder(&dest) + + err := enc.Encode(src) + if err != nil { + return nil + } + + var result *flight.FlightData + err = dec.Decode(&result) + if err != nil { + return nil + } + + return result +} + +func (mgs *mockDoGetServer) Send(data *flight.FlightData) error { + nlog.Debugf("mockDoGetServer.Send") + mgs.dataList = append(mgs.dataList, deepCopy(data)) + return nil +} + +type mockDoPutServer struct { + grpc.ServerStream + nextDataList []*flight.FlightData +} + +func (mps *mockDoPutServer) Send(*flight.PutResult) error { + return nil +} + +type mockDoActionServer struct { + grpc.ServerStream +} + +func (*mockDoActionServer) Send(*flight.Result) error { + return nil +} + +func (mps *mockDoPutServer) Recv() (*flight.FlightData, error) { + if len(mps.nextDataList) == 0 { + return nil, io.EOF + } + nlog.Debugf("mockDoPutServer.Recv") + data := mps.nextDataList[0] + mps.nextDataList = mps.nextDataList[1:] + return data, nil +} + +func TestNewDataMeshFlightHandler(t *testing.T) { + t.Parallel() + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{ + { + Mode: string(config.ModeDirect), + DataSourceTypes: []string{"odps"}, + Endpoint: "127.0.0.1:10000", + }, + }) + assert.NotNil(t, svr) + + dm := svr.(*datameshFlightHandler) + assert.NotNil(t, dm) + assert.Greater(t, len(dm.customHandles), 4) + + assert.Equal(t, 3, len(dm.iochannels)) + assert.NotNil(t, dm.iochannels["localfs"]) + assert.NotNil(t, dm.iochannels["oss"]) + assert.NotNil(t, dm.iochannels["odps"]) + assert.NotNil(t, dm.cmds) +} + +func TestGetFlightInf_FAILED(t *testing.T) { + t.Parallel() + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + + // anyCmd.UnmarshalNew failed + info, err := svr.GetFlightInfo(context.Background(), &flight.FlightDescriptor{}) + assert.Error(t, err) + assert.Nil(t, info) + + // proto.Unmarshal failed + info, err = svr.GetFlightInfo(context.Background(), &flight.FlightDescriptor{ + Cmd: []byte("xyz"), + }) + assert.Error(t, err) + assert.Nil(t, info) + + // NewDataMeshRequestContext failed + desc, err := DescForCommand(&datamesh.CommandGetDomainDataSchema{}) + assert.NoError(t, err) + info, err = svr.GetFlightInfo(context.Background(), desc) + assert.Error(t, err) + assert.Nil(t, info) +} + +func TestGetFlightInf_DataSource_Invalidate(t *testing.T) { + t.Parallel() + + filename := fmt.Sprintf("localtest-%s.txt", uuid.New().String()) + + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + svr := NewDataMeshFlightHandler(domainDataService, datasourceService, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + + // datasource not registed + domainDataID := registDomainData(t, conf, "test-datasouce", filename) + desc, err := DescForCommand(&datamesh.CommandDomainDataQuery{ + DomaindataId: domainDataID, + }) + assert.NoError(t, err) + info, err := svr.GetFlightInfo(context.Background(), desc) + assert.Error(t, err) + assert.Nil(t, info) + + // reigst datasource but type is invalidate + lfs, err := json.Marshal(&datamesh.DataSourceInfo{}) + assert.NoError(t, err) + + strConfig, err := tls.EncryptOAEP(&conf.DomainKey.PublicKey, lfs) + assert.NoError(t, err) + + _, err = conf.KusciaClient.KusciaV1alpha1().DomainDataSources(conf.KubeNamespace).Create(context.Background(), &v1alpha1.DomainDataSource{ + ObjectMeta: v1.ObjectMeta{ + Name: "test-datasouce", + }, + Spec: v1alpha1.DomainDataSourceSpec{ + Name: "test-datasouce", + Type: "odps", + Data: map[string]string{ + "encryptedInfo": strConfig, + }, + }, + }, v1.CreateOptions{}) + + assert.NoError(t, err) + + info, err = svr.GetFlightInfo(context.Background(), desc) + assert.Error(t, err) // not found channel + assert.Nil(t, info) +} + +func TestGetFlightInf_Success(t *testing.T) { + t.Parallel() + + filename := fmt.Sprintf("localtest-%s.txt", uuid.New().String()) + + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + svr := NewDataMeshFlightHandler(domainDataService, datasourceService, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + + registLocalFileDomainDataSource(t, conf, common.DefaultDataSourceID) + domainDataID := registDomainData(t, conf, common.DefaultDataSourceID, filename) + desc, err := DescForCommand(&datamesh.CommandDomainDataQuery{ + DomaindataId: domainDataID, + }) + assert.NoError(t, err) + info, err := svr.GetFlightInfo(context.Background(), desc) + assert.NoError(t, err) + assert.NotNil(t, info) + + assert.Len(t, info.Endpoint, 1) + assert.NotNil(t, info.Endpoint[0]) + assert.Len(t, info.Endpoint[0].Location, 1) + assert.NotNil(t, info.Endpoint[0].Location[0]) + assert.NotEmpty(t, info.Endpoint[0].Location[0].Uri) + assert.NotNil(t, info.Endpoint[0].Ticket) + assert.NotEmpty(t, info.Endpoint[0].Ticket.Ticket) + + dm := svr.(*datameshFlightHandler) + assert.NotNil(t, dm) + + itm, ok := dm.cmds.Get(string(info.Endpoint[0].Ticket.Ticket)) + assert.True(t, ok) + assert.NotNil(t, itm) + + rc := itm.(*DataMeshRequestContext) + assert.NotNil(t, rc) + assert.NotNil(t, rc.query) + assert.NotNil(t, rc.io) + assert.Equal(t, domainDataID, rc.query.DomaindataId) +} + +func TestDoGet_InvalidateTicket(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + + assert.Error(t, svr.DoGet(&flight.Ticket{ + Ticket: []byte("invalidate-ticket"), + }, nil)) +} + +func TestDoGet_Success(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + dm := svr.(*datameshFlightHandler) + assert.NotNil(t, dm) + + assert.NoError(t, dm.cmds.Add("test-ticket", &DataMeshRequestContext{ + query: &datamesh.CommandDomainDataQuery{ + ContentType: datamesh.ContentType_RAW, + }, + io: &mockIOChannel{}, + }, time.Minute)) + + assert.NoError(t, svr.DoGet(&flight.Ticket{ + Ticket: []byte("test-ticket"), + }, &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + })) +} + +func TestDoPut_Failed(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + dm := svr.(*datameshFlightHandler) + assert.NotNil(t, dm) + + // create reader failed + assert.Error(t, svr.DoPut(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: []*flight.FlightData{}, + })) + + mgs := &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + } + w := flight.NewRecordWriter(mgs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + assert.NotNil(t, w) + w.Close() + assert.NotEmpty(t, mgs.dataList) + nlog.Infof("writen data count=%d", len(mgs.dataList)) + // no desc + assert.Error(t, svr.DoPut(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: mgs.dataList, + })) + + dataList := mgs.dataList + dataList[0].FlightDescriptor = &flight.FlightDescriptor{ + Cmd: []byte("not-exists-ticket"), + } + + assert.Error(t, svr.DoPut(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: mgs.dataList, + })) +} + +func TestDoPut_Success(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + dm := svr.(*datameshFlightHandler) + assert.NotNil(t, dm) + + mgs := &mockDoGetServer{ + ServerStream: &mockGrpcServerStream{}, + } + w := flight.NewRecordWriter(mgs, ipc.WithSchema(GenerateBinaryDataArrowSchema())) + assert.NotNil(t, w) + w.Close() + assert.NotEmpty(t, mgs.dataList) + + assert.Len(t, mgs.dataList, 1) + + dataList := mgs.dataList + dataList[0].FlightDescriptor = &flight.FlightDescriptor{ + Cmd: []byte("test-ticket"), + } + + assert.NoError(t, dm.cmds.Add("test-ticket", &DataMeshRequestContext{ + query: &datamesh.CommandDomainDataQuery{ + ContentType: datamesh.ContentType_RAW, + }, + io: &mockIOChannel{}, + }, time.Minute)) + + assert.NoError(t, svr.DoPut(&mockDoPutServer{ + ServerStream: &mockGrpcServerStream{}, + nextDataList: mgs.dataList, + })) +} + +func TestDoAction_Failed(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + + assert.Error(t, svr.DoAction(&flight.Action{ + Type: "not-exists", + }, nil)) + + // recover + assert.Error(t, svr.DoAction(&flight.Action{ + Type: "ActionCreateDomainDataRequest", + }, nil)) +} + +func TestDoAction_Success(t *testing.T) { + t.Parallel() + + conf := initContextTestEnv(t) + domainDataService := service.NewDomainDataService(conf) + datasourceService := service.NewDomainDataSourceService(conf, nil) + + assert.NotNil(t, domainDataService) + assert.NotNil(t, datasourceService) + + svr := NewDataMeshFlightHandler(domainDataService, datasourceService, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + + body, _ := proto.Marshal(&datamesh.CreateDomainDataRequest{ + DomaindataId: "test-domain-data", + }) + + // datasource not registed + assert.Error(t, svr.DoAction(&flight.Action{ + Type: "ActionCreateDomainDataRequest", + Body: body, + }, nil)) + + registLocalFileDomainDataSource(t, conf, common.DefaultDataSourceID) + + body, _ = proto.Marshal(&datamesh.CreateDomainDataRequest{ + DomaindataId: "test-domain-data", + DatasourceId: common.DefaultDataSourceID, + }) + + assert.NoError(t, svr.DoAction(&flight.Action{ + Type: "ActionCreateDomainDataRequest", + Body: body, + }, &mockDoActionServer{})) +} + +func TestGetFlightInfo_recover(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + info, err := svr.GetFlightInfo(context.Background(), nil) + assert.Error(t, err) + assert.Nil(t, info) +} + +func TestDoGet_recover(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + + assert.Error(t, svr.DoGet(nil, nil)) +} + +func TestDoPut_recover(t *testing.T) { + t.Parallel() + + svr := NewDataMeshFlightHandler(nil, nil, []config.ExternalDataProxyConfig{}) + assert.NotNil(t, svr) + + assert.Error(t, svr.DoPut(nil)) +} diff --git a/pkg/datamesh/dmflight/oss_uploader.go b/pkg/datamesh/dmflight/oss_uploader.go new file mode 100644 index 00000000..af72c730 --- /dev/null +++ b/pkg/datamesh/dmflight/oss_uploader.go @@ -0,0 +1,288 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "bytes" + "context" + "errors" + "fmt" + "path" + "sync" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3" + + "github.com/secretflow/kuscia/pkg/utils/nlog" +) + +type OSSUploader struct { + loggerKey string + buffer *bytes.Buffer + + partSizeBytes int + + client *s3.S3 + bucket string + objectKey string + + mutex sync.Mutex + isFinished bool + uploadLastError error + isUploadStarted bool + wg sync.WaitGroup + + multiPartUploadChan chan []byte + uploadContext context.Context + uploadCancel context.CancelFunc +} + +func NewOSSUploader(ctx context.Context, client *s3.S3, bucket, objectKey string, partSizeBytes int) *OSSUploader { + initBuf := make([]byte, partSizeBytes) + ctx, cancel := context.WithCancel(ctx) + return &OSSUploader{ + client: client, + partSizeBytes: partSizeBytes, + buffer: bytes.NewBuffer(initBuf[:0]), + isFinished: false, + bucket: bucket, + objectKey: objectKey, + multiPartUploadChan: make(chan []byte, 3), + uploadContext: ctx, + uploadCancel: cancel, + loggerKey: path.Join(bucket, objectKey), + } +} + +func (ow *OSSUploader) Write(p []byte) (int, error) { + if ow.buffer.Len()+len(p) >= ow.partSizeBytes { + // buffer is full, upload and clean buffer + if ow.buffer.Len() == 0 { + // current buffer is not used, so just upload the body + ow.appendBuffer(p) + } else { + if _, err := ow.buffer.Write(p); err != nil { + nlog.Warnf("Write to buffer failed with %s", err.Error()) + return 0, err + } + + ow.appendBuffer(ow.buffer.Bytes()) + ow.buffer.Reset() + } + + return len(p), nil + } + + // buffer is not full, so cache it + return ow.buffer.Write(p) +} + +func (ow *OSSUploader) appendBuffer(p []byte) { + buf := make([]byte, len(p)) + copy(buf, p) + ow.multiPartUploadChan <- buf + + ow.mutex.Lock() + defer ow.mutex.Unlock() + + if !ow.isUploadStarted { + ow.isUploadStarted = true + ow.wg.Add(1) + go func() { + defer ow.wg.Done() + err := ow.multiPartUpload() + + if err != nil { + ow.mutex.Lock() + defer ow.mutex.Unlock() + ow.uploadLastError = err + } + }() + } +} + +func (ow *OSSUploader) multiPartUpload() error { + result, err := ow.client.CreateMultipartUpload(&s3.CreateMultipartUploadInput{ + Bucket: aws.String(ow.bucket), + Key: aws.String(ow.objectKey), + }) + if err != nil { + nlog.Warnf("[%s] CreateMultipartUpload failed with: %s", ow.loggerKey, err.Error()) + return err + } + + nlog.Infof("[%s] Multipart UploadID=%s", ow.loggerKey, *result.UploadId) + + uploadID := result.UploadId + nextPartNumber := int64(1) + + var parts []*s3.CompletedPart +loopForPart: + for { + select { + case buf := <-ow.multiPartUploadChan: + part, err := ow.uploadToOSS(buf, uploadID, nextPartNumber) + if err != nil { + return err + } + nextPartNumber++ + parts = append(parts, part) + case <-ow.uploadContext.Done(): + nlog.Infof("[%s] Got finished signal", ow.loggerKey) + break loopForPart + } + } + +loopLeftData: + for { // upload left data + select { + case buf := <-ow.multiPartUploadChan: + part, err := ow.uploadToOSS(buf, uploadID, nextPartNumber) + if err != nil { + return err + } + nextPartNumber++ + parts = append(parts, part) + default: + break loopLeftData + } + + } + + nlog.Infof("[%s] Try to complete upload", ow.loggerKey) + + if _, err := ow.client.CompleteMultipartUpload(&s3.CompleteMultipartUploadInput{ + Bucket: aws.String(ow.bucket), + Key: aws.String(ow.objectKey), + UploadId: uploadID, + MultipartUpload: &s3.CompletedMultipartUpload{ + Parts: parts, + }, + }); err != nil { + nlog.Warnf("[%s] Failed to complete Multipart Upload: %s", ow.loggerKey, err.Error()) + return err + } + + return nil +} + +func (ow *OSSUploader) uploadToOSS(p []byte, uploadID *string, partNumber int64) (*s3.CompletedPart, error) { + nlog.Infof("[%s] Try to upload part %d, len=%d", ow.loggerKey, partNumber, len(p)) + result, err := ow.client.UploadPart(&s3.UploadPartInput{ + Bucket: aws.String(ow.bucket), + Key: aws.String(ow.objectKey), + UploadId: uploadID, + PartNumber: aws.Int64(partNumber), + Body: bytes.NewReader(p), + }) + if err != nil { + nlog.Warnf("[%s] Failed to upload part(%s:%d): %s", ow.loggerKey, *uploadID, partNumber, err) + + return nil, err + } + + return &s3.CompletedPart{ + ETag: result.ETag, + PartNumber: aws.Int64(int64(partNumber)), + }, nil +} + +func (ow *OSSUploader) hadFinished() error { + ow.mutex.Lock() + defer ow.mutex.Unlock() + if ow.isFinished { + return errors.New("had called FinishUpload") + } + ow.isFinished = true + + return nil +} + +func (ow *OSSUploader) hadUploadError() error { + ow.mutex.Lock() + defer ow.mutex.Unlock() + + if ow.uploadLastError != nil { + return fmt.Errorf("some upload failed with %s", ow.uploadLastError.Error()) + } + return nil +} + +func (ow *OSSUploader) isNeedUploadLeftBuffer() (bool, error) { + ow.mutex.Lock() + defer ow.mutex.Unlock() + + if ow.buffer.Len() != 0 { + if ow.isUploadStarted { + return true, nil + } + + nlog.Infof("[%s] Buffer is small than %d bytes, so just upload all", ow.loggerKey, ow.partSizeBytes) + // try to upload total buffer without multi-party + reader := bytes.NewReader(ow.buffer.Bytes()) + _, err := ow.client.PutObject(&s3.PutObjectInput{ + Bucket: aws.String(ow.bucket), + Key: aws.String(ow.objectKey), + Body: reader, + }) + + if err != nil { + nlog.Warnf("[%s] PutObject failed with %s", ow.loggerKey, err.Error()) + ow.uploadLastError = err + } + + return false, err + + } + + return false, nil +} + +func (ow *OSSUploader) FinishUpload() error { + if err := ow.hadFinished(); err != nil { + return err + } + + started, err := ow.isNeedUploadLeftBuffer() + if err != nil { + return err + } + + if started { + ow.appendBuffer(ow.buffer.Bytes()) + } + + nlog.Infof("[%s] Signal to finish upload", ow.loggerKey) + // wait for all upload finished + ow.uploadCancel() + ow.wg.Wait() + + if err := ow.hadUploadError(); err != nil { + nlog.Warnf("[%s] Upload failed with some error, so don't call complete upload", ow.loggerKey) + return err + } + + nlog.Infof("[%s] Finish upload", ow.loggerKey) + + return nil +} + +func (ow *OSSUploader) Close() error { + go func() { + ow.FinishUpload() + }() + + return nil +} diff --git a/pkg/datamesh/dmflight/oss_uploader_test.go b/pkg/datamesh/dmflight/oss_uploader_test.go new file mode 100644 index 00000000..0f831410 --- /dev/null +++ b/pkg/datamesh/dmflight/oss_uploader_test.go @@ -0,0 +1,271 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "context" + "net/http/httptest" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/johannesboyne/gofakes3" + "github.com/johannesboyne/gofakes3/backend/s3mem" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/wait" +) + +func TestOssWriter_New(t *testing.T) { + t.Parallel() + writer := NewOSSUploader(context.Background(), nil, "xyz", "test.raw", 2*1024) + assert.NotNil(t, writer) + assert.Nil(t, writer.client) + assert.Equal(t, "xyz", writer.bucket) + assert.Equal(t, "test.raw", writer.objectKey) + assert.Equal(t, 2*1024, writer.partSizeBytes) + assert.False(t, writer.isFinished) + assert.Nil(t, writer.uploadLastError) + assert.NotNil(t, writer.buffer) + assert.Equal(t, 0, writer.buffer.Len()) +} + +func TestOssWriter_Write_Cache(t *testing.T) { + t.Parallel() + + ow := NewOSSUploader(context.Background(), nil, "xyz", "test.raw", 2*1024) + n, err := ow.Write([]byte("abcdefg")) + assert.NoError(t, err) + assert.Equal(t, len("abcdefg"), n) + assert.Nil(t, ow.uploadLastError) + assert.Equal(t, n, ow.buffer.Len()) + assert.Equal(t, "abcdefg", ow.buffer.String()) + assert.False(t, ow.isFinished) +} + +func initMockOssBackend(t *testing.T) (*s3mem.Backend, *session.Session, *httptest.Server) { + backend := s3mem.New() + faker := gofakes3.New(backend) + ts := httptest.NewServer(faker.Server()) + + assert.NoError(t, backend.CreateBucket("xyz")) + + region := "minio" + sess, err := session.NewSession(&aws.Config{ + Credentials: credentials.NewStaticCredentials("testid", "testkey", ""), + Endpoint: &ts.URL, + Region: ®ion, + S3ForcePathStyle: aws.Bool(true), + }) + assert.NoError(t, err) + assert.NotNil(t, sess) + + return backend, sess, ts +} + +func TestOssWriter_Write_MultiUpload(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "xyz", "test.raw", 128) + + // split to two part: 128, 72. + n, err := ow.Write(make([]byte, 200)) + assert.NoError(t, err) + assert.Equal(t, 200, n) + assert.Equal(t, 0, ow.buffer.Len()) + + assert.False(t, ow.isFinished) + assert.True(t, ow.isUploadStarted) + assert.NoError(t, ow.FinishUpload()) + obj, err := backend.GetObject("xyz", "test.raw", nil) + assert.NoError(t, err) + assert.NotNil(t, obj) + + assert.True(t, ow.isFinished) + assert.True(t, ow.isUploadStarted) + assert.Equal(t, int64(200), obj.Size) +} + +func TestOssWriter_FinishUpload_TotalUpload(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "xyz", "test.raw", 1024) + n, err := ow.Write(make([]byte, 100)) + assert.NoError(t, err) + assert.Equal(t, 100, n) + + assert.False(t, ow.isFinished) + assert.NoError(t, ow.FinishUpload()) + obj, err := backend.GetObject("xyz", "test.raw", nil) + assert.NoError(t, err) + assert.NotNil(t, obj) + + assert.True(t, ow.isFinished) + assert.False(t, ow.isUploadStarted) + assert.Equal(t, int64(100), obj.Size) +} + +func TestOssWriter_FinishUpload_RetryTimes(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "xyz", "test.raw", 1024) + n, err := ow.Write(make([]byte, 100)) + assert.NoError(t, err) + assert.Equal(t, 100, n) + + assert.False(t, ow.isFinished) + assert.NoError(t, ow.FinishUpload()) + obj, err := backend.GetObject("xyz", "test.raw", nil) + assert.NoError(t, err) + assert.NotNil(t, obj) + + assert.Error(t, ow.FinishUpload()) +} + +func TestOssWriter_FinishUpload_WithError(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "no-exists", "test.raw", 1024) + n, err := ow.Write(make([]byte, 100)) + assert.NoError(t, err) + assert.Equal(t, 100, n) + + assert.False(t, ow.isFinished) + assert.Error(t, ow.FinishUpload()) + _, err = backend.GetObject("xyz", "test.raw", nil) + assert.Error(t, err) + + assert.Error(t, ow.uploadLastError) +} + +func TestOssWriter_MultiPartUpload_WithError_1(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "no-exists", "test.raw", 128) + n, err := ow.Write(make([]byte, 200)) + assert.NoError(t, err) + assert.Equal(t, 200, n) + + assert.False(t, ow.isFinished) + assert.Error(t, ow.FinishUpload()) + _, err = backend.GetObject("xyz", "test.raw", nil) + assert.Error(t, err) + + assert.Error(t, ow.uploadLastError) +} + +func TestOssWriter_MultiPartUpload_WithError_2(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "xyz", "test.raw", 128) + n, err := ow.Write(make([]byte, 200)) + assert.NoError(t, err) + assert.Equal(t, 200, n) + + // wait first part uploaded + assert.NoError(t, wait.PollImmediate(10*time.Millisecond, time.Second, func() (bool, error) { + return ow.isUploadStarted, nil + })) + time.Sleep(100 * time.Millisecond) + + // hack: change bucket name, force upload failed + ow.bucket = "not-exists" + n, err = ow.Write(make([]byte, 200)) + assert.NoError(t, err) + assert.Equal(t, 200, n) + + assert.False(t, ow.isFinished) + assert.Error(t, ow.FinishUpload()) + _, err = backend.GetObject("xyz", "test.raw", nil) + assert.Error(t, err) + + assert.Error(t, ow.uploadLastError) +} + +func TestOssWriter_MultiPartUpload_WithError_3(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "xyz", "test.raw", 128) + n, err := ow.Write(make([]byte, 200)) + assert.NoError(t, err) + assert.Equal(t, 200, n) + + // wait first part uploaded + assert.NoError(t, wait.PollImmediate(10*time.Millisecond, time.Second, func() (bool, error) { + return ow.isUploadStarted, nil + })) + time.Sleep(100 * time.Millisecond) + + // hack: change bucket name, force complete request failed + ow.bucket = "not-exists" + assert.False(t, ow.isFinished) + assert.Error(t, ow.FinishUpload()) + _, err = backend.GetObject("xyz", "test.raw", nil) + assert.Error(t, err) + + assert.Error(t, ow.uploadLastError) +} + +func TestOssWriter_MultiPartUpload_NoLeftData(t *testing.T) { + t.Parallel() + + backend, sess, ts := initMockOssBackend(t) + defer ts.Close() + + ow := NewOSSUploader(context.Background(), s3.New(sess), "xyz", "test.raw", 128) + n, err := ow.Write(make([]byte, 100)) + assert.NoError(t, err) + assert.Equal(t, 100, n) + + n, err = ow.Write(make([]byte, 28)) + assert.NoError(t, err) + assert.Equal(t, 28, n) + + assert.False(t, ow.isFinished) + assert.True(t, ow.isUploadStarted) + assert.Equal(t, 0, ow.buffer.Len()) + + assert.NoError(t, ow.FinishUpload()) + obj, err := backend.GetObject("xyz", "test.raw", nil) + assert.NoError(t, err) + assert.NotNil(t, obj) + + assert.True(t, ow.isFinished) + assert.True(t, ow.isUploadStarted) + assert.Equal(t, int64(128), obj.Size) +} diff --git a/pkg/datamesh/dmflight/utils.go b/pkg/datamesh/dmflight/utils.go new file mode 100644 index 00000000..3129f861 --- /dev/null +++ b/pkg/datamesh/dmflight/utils.go @@ -0,0 +1,132 @@ +// Copyright 2023 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "fmt" + "net/url" + "strings" + + "github.com/apache/arrow/go/v13/arrow" + "github.com/apache/arrow/go/v13/arrow/flight" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/secretflow/kuscia/pkg/common" + "github.com/secretflow/kuscia/pkg/utils/nlog" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +const AliyunOssEndpointSuffix string = ".aliyuncs.com" +const BuiltinFlightServerEndpointURI string = "kuscia://datamesh" + +func DescForCommand(cmd proto.Message) (*flight.FlightDescriptor, error) { + var any anypb.Any + if err := any.MarshalFrom(cmd); err != nil { + return nil, err + } + + data, err := proto.Marshal(&any) + if err != nil { + return nil, err + } + return &flight.FlightDescriptor{ + Type: flight.DescriptorCMD, + Cmd: data, + }, nil +} + +func CreateDateMeshFlightInfo(tick []byte, endpointURI string) *flight.FlightInfo { + info := &flight.FlightInfo{ + FlightDescriptor: &flight.FlightDescriptor{ + Type: flight.DescriptorCMD, + Cmd: []byte{}, + Path: []string{}, + }, + Endpoint: []*flight.FlightEndpoint{ + { + Location: []*flight.Location{ + { + Uri: endpointURI, + }, + }, + Ticket: &flight.Ticket{ + Ticket: tick, + }, + }, + }, + } + + return info +} + +// GenerateArrowSchema generate schema from domaindata +func GenerateArrowSchema(domainData *datamesh.DomainData) (*arrow.Schema, error) { + // get schema from query + fields := make([]arrow.Field, 0) + for i, column := range domainData.Columns { + colType := common.Convert2ArrowColumnType(column.Type) + if colType == nil { + return nil, status.Errorf(codes.Internal, "invalid column(%s) with type(%s)", column.Name, column.Type) + } + fields = append(fields, arrow.Field{ + Name: column.Name, + Type: colType, + Nullable: !column.NotNullable, + }) + nlog.Debugf("Columns[%d:%s].Nullable is :%v", i, column.Name, !column.NotNullable) + } + return arrow.NewSchema(fields, nil), nil +} + +func GenerateBinaryDataArrowSchema() *arrow.Schema { + return arrow.NewSchema([]arrow.Field{ + { + Name: "data", + Type: arrow.BinaryTypes.Binary, + Nullable: false, + }, + }, nil) +} + +func PackActionResult(msg proto.Message) (*flight.Result, error) { + var err error + + ret := &flight.Result{} + if ret.Body, err = proto.Marshal(msg); err != nil { + nlog.Warnf("Unable to marshal msg to flight.Result.Body: %v", err) + return nil, status.Errorf(codes.Internal, fmt.Sprintf("Unable to marshal final response: %v", err)) + } + return ret, nil +} + +func ParseRegionFromEndpoint(endpoint string) (string, error) { + if strings.Contains(endpoint, AliyunOssEndpointSuffix) { + // aliyun oss + url, err := url.Parse(endpoint) + if err != nil { + nlog.Warnf("Parse endpoint(%s) failed", endpoint) + return "", err + } + + if strings.HasSuffix(url.Host, AliyunOssEndpointSuffix) { + return strings.TrimSuffix(url.Host, AliyunOssEndpointSuffix), nil + } + } + + return "", nil +} diff --git a/pkg/datamesh/dmflight/utils_test.go b/pkg/datamesh/dmflight/utils_test.go new file mode 100644 index 00000000..4f4321aa --- /dev/null +++ b/pkg/datamesh/dmflight/utils_test.go @@ -0,0 +1,153 @@ +// Copyright 2023 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dmflight + +import ( + "testing" + + "github.com/apache/arrow/go/v13/arrow" + "github.com/apache/arrow/go/v13/arrow/flight" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/secretflow/kuscia/proto/api/v1alpha1" + "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" +) + +func TestGenerateArrowSchema_ValidSchema(t *testing.T) { + t.Parallel() + data := &datamesh.DomainData{ + Columns: []*v1alpha1.DataColumn{ + {Name: "id", Type: "int64", NotNullable: true}, + {Name: "name", Type: "string", NotNullable: false}, + }, + } + + expectedFields := []arrow.Field{ + {Name: "id", Type: arrow.PrimitiveTypes.Int64, Nullable: false}, + {Name: "name", Type: arrow.BinaryTypes.String, Nullable: true}, + } + + schema, err := GenerateArrowSchema(data) + assert.NoError(t, err) + assert.Equal(t, expectedFields, schema.Fields()) +} + +func TestGenerateArrowSchema_InValidSchema(t *testing.T) { + t.Parallel() + data := &datamesh.DomainData{ + Columns: []*v1alpha1.DataColumn{ + {Name: "invalid", Type: "unknown", NotNullable: true}, + }, + } + + _, err := GenerateArrowSchema(data) + assert.Error(t, err) + assert.Equal(t, codes.Internal, status.Code(err)) +} + +func TestGenerateArrowSchema_EmptySchema(t *testing.T) { + t.Parallel() + data := &datamesh.DomainData{ + Columns: []*v1alpha1.DataColumn{}, + } + + expectedFields := []arrow.Field{} + + schema, err := GenerateArrowSchema(data) + assert.NoError(t, err) + assert.Equal(t, expectedFields, schema.Fields()) +} + +func TestCreateDateMeshFlightInfo(t *testing.T) { + t.Parallel() + info := CreateDateMeshFlightInfo([]byte("xyz"), "url") + + assert.NotNil(t, info) + assert.Equal(t, "url", info.Endpoint[0].Location[0].Uri) + assert.Equal(t, "xyz", string(info.Endpoint[0].Ticket.Ticket)) + assert.Equal(t, flight.DescriptorCMD, info.FlightDescriptor.Type) +} + +func TestParseRegionFromEndpoint(t *testing.T) { + t.Parallel() + region, err := ParseRegionFromEndpoint("xyz") + assert.NoError(t, err) + assert.Equal(t, "", region) + + region, err = ParseRegionFromEndpoint("http://shanghai.aliyuncs.com/") + assert.NoError(t, err) + assert.Equal(t, "shanghai", region) + + region, err = ParseRegionFromEndpoint("http://shanghai.aliyuncs.com.cn:9000/") + assert.NoError(t, err) + assert.Equal(t, "", region) + + region, err = ParseRegionFromEndpoint("http://shanghai.aliyuncs.com.cn/") + assert.NoError(t, err) + assert.Equal(t, "", region) + + region, err = ParseRegionFromEndpoint("http://shanghai.aliyuncs.com.cn/http://xyz") + assert.NoError(t, err) + assert.Equal(t, "", region) +} + +func TestPackActionResult(t *testing.T) { + t.Parallel() + r, err := PackActionResult(nil) + assert.NoError(t, err) + assert.NotNil(t, r) +} + +func TestGenerateBinaryDataArrowSchema(t *testing.T) { + t.Parallel() + schema := GenerateBinaryDataArrowSchema() + assert.NotNil(t, schema) + + assert.NotNil(t, schema.Fields()) + assert.Equal(t, 1, len(schema.Fields())) + assert.Equal(t, arrow.BinaryTypes.Binary, schema.Fields()[0].Type) +} + +func TestDescForCommand(t *testing.T) { + t.Parallel() + desc, err := DescForCommand(nil) + + assert.Error(t, err) + assert.Nil(t, desc) + + desc, err = DescForCommand(&datamesh.CommandDomainDataQuery{ + DomaindataId: "xyz", + }) + assert.NoError(t, err) + assert.NotNil(t, desc) + assert.Equal(t, flight.DescriptorCMD, desc.Type) + + var any anypb.Any + err = proto.Unmarshal(desc.Cmd, &any) + assert.NoError(t, err) + + msg, err := any.UnmarshalNew() + assert.NoError(t, err) + assert.NotNil(t, msg) + + m, ok := msg.(*datamesh.CommandDomainDataQuery) + assert.True(t, ok) + assert.NotNil(t, m) + assert.Equal(t, "xyz", m.DomaindataId) +} diff --git a/pkg/datamesh/errorcode/error_code.go b/pkg/datamesh/errorcode/error_code.go index 9e029cf5..bc7cff44 100644 --- a/pkg/datamesh/errorcode/error_code.go +++ b/pkg/datamesh/errorcode/error_code.go @@ -17,56 +17,25 @@ package errorcode import ( "k8s.io/apimachinery/pkg/api/errors" - "github.com/secretflow/kuscia/pkg/web/errorcode" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) -const ( - ErrRequestInvalidate = 12100 - ErrForUnexpected = 12101 - - ErrCreateDomainData = 12200 - ErrQueryDomainData = 12201 - ErrGetDomainDataFromKubeFailed = 12202 - ErrMergeDomainDataFailed = 12203 - ErrPatchDomainDataFailed = 12204 - ErrDeleteDomainDataFailed = 12205 - - ErrCreateDomainDataSource = 12300 - ErrParseDomainDataSourceFailed = 12301 - ErrQueryDomainDataSource = 12302 - ErrDeleteDomainDataSourceFailed = 12303 - ErrDomainDataSourceExists = 12304 - ErrDomainDataSourceNotExists = 12305 - ErrGetDomainDataSourceFromKubeFailed = 12306 - ErrDecodeDomainDataSourceInfoFailed = 12307 - ErrEncodeDomainDataSourceInfoFailed = 12308 - ErrMergeDomainDataSourceFailed = 12309 - ErrPatchDomainDataSourceFailed = 12310 - - ErrCreateDomainDataGrant = 12400 - ErrUpdateDomainDataGrant = 12401 - ErrQueryDomainDataGrant = 12402 - ErrDeleteDomainDataGrant = 12403 - ErrDomainDataGrantExists = 12404 - ErrDomainDataGrantNotExists = 12405 -) - -func GetDomainDataSourceErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func GetDomainDataSourceErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainDataSourceNotExists + return errorcode.ErrorCode_DataMeshErrDomainDataSourceNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataSourceExists + return errorcode.ErrorCode_DataMeshErrDomainDataSourceExists } return defaultErrorCode } -func GetDomainDataGrantErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func GetDomainDataGrantErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainDataGrantNotExists + return errorcode.ErrorCode_DataMeshErrDomainDataGrantNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataGrantExists + return errorcode.ErrorCode_DataMeshErrDomainDataGrantExists } return defaultErrorCode } diff --git a/pkg/datamesh/flight/dataproxy_client.go b/pkg/datamesh/flight/dataproxy_client.go deleted file mode 100644 index f925ad75..00000000 --- a/pkg/datamesh/flight/dataproxy_client.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package flight - -import ( - "context" - "crypto/tls" - "time" - - "github.com/apache/arrow/go/v13/arrow/flight" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/keepalive" - - "github.com/secretflow/kuscia/pkg/datamesh/config" - "github.com/secretflow/kuscia/pkg/utils/kusciaconfig" - "github.com/secretflow/kuscia/pkg/utils/nlog" - tlsutils "github.com/secretflow/kuscia/pkg/utils/tls" - "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" -) - -const ( - dpRequestTimeout = time.Second * 180 -) - -type DataServer interface { - GetFlightInfoDataMeshQuery(context.Context, *datamesh.CommandDataMeshQuery) (*flight.FlightInfo, error) - GetFlightInfoDataMeshUpdate(context.Context, *datamesh.CommandDataMeshUpdate) (*flight.FlightInfo, error) -} - -type DataProxyConfig struct { - Addr string - ClientTLSConfig *kusciaconfig.TLSConfig -} - -type DataProxyClient struct { - conn *grpc.ClientConn - addr string - dialOpts []grpc.DialOption - flightClient flight.Client -} - -func buildGrpcOptions(clientTLSConfig *tls.Config) []grpc.DialOption { - dialOpts := []grpc.DialOption{ - grpc.WithKeepaliveParams(keepalive.ClientParameters{ - Time: 1 * time.Minute, - Timeout: 30 * time.Second, - PermitWithoutStream: true, - }), - } - if clientTLSConfig != nil { - credentials := credentials.NewTLS(clientTLSConfig) - dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials)) - } else { - dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - return dialOpts -} - -func NewDataProxyClient(conf *config.ExternalDataProxyConfig) (*DataProxyClient, error) { - var ( - err error - conn *grpc.ClientConn - nativeTLSConfig *tls.Config - ) - - if conf.ClientTLSConfig != nil { - if nativeTLSConfig, err = tlsutils.BuildClientTLSConfigViaPath(conf.ClientTLSConfig.CAFile, - conf.ClientTLSConfig.CertFile, conf.ClientTLSConfig.KeyFile); err != nil { - return nil, err - } - } - - dialOpts := buildGrpcOptions(nativeTLSConfig) - if conn, err = grpc.Dial(conf.Endpoint, dialOpts...); err != nil { - nlog.Errorf("create grpc conn to %s fail: %v", conf.Endpoint, err) - return nil, err - } - - flightClient := flight.NewClientFromConn(conn, nil) - - return &DataProxyClient{ - conn: conn, - addr: conf.Endpoint, - dialOpts: dialOpts, - flightClient: flightClient, - }, nil -} - -func (dp *DataProxyClient) GetFlightInfoDataMeshQuery(ctx context.Context, - query *datamesh.CommandDataMeshQuery) (*flight.FlightInfo, error) { - dpDesc, err := DescForCommand(query) - if err != nil { - nlog.Errorf("Generate Descriptor to dataproxy fail, %v", err) - return nil, buildGrpcErrorf(nil, codes.Internal, "Generate Descriptor to dataproxy fail") - } - - dpCtx, cancel := context.WithTimeout(ctx, dpRequestTimeout) - defer cancel() - - flightInfo, err := dp.flightClient.GetFlightInfo(dpCtx, dpDesc) - if err != nil { - return nil, err - } - - return flightInfo, nil -} - -func (dp *DataProxyClient) GetFlightInfoDataMeshUpdate(ctx context.Context, - query *datamesh.CommandDataMeshUpdate) (*flight.FlightInfo, error) { - dpDesc, err := DescForCommand(query) - if err != nil { - nlog.Errorf("Generate Descriptor to dataproxy fail, %v", err) - return nil, buildGrpcErrorf(nil, codes.Internal, "Generate Descriptor to dataproxy fail") - } - - dpCtx, cancel := context.WithTimeout(ctx, dpRequestTimeout) - defer cancel() - - flightInfo, err := dp.flightClient.GetFlightInfo(dpCtx, dpDesc) - if err != nil { - return nil, err - } - - return flightInfo, nil -} diff --git a/pkg/datamesh/flight/dataproxy_client_test.go b/pkg/datamesh/flight/dataproxy_client_test.go deleted file mode 100644 index 694fa627..00000000 --- a/pkg/datamesh/flight/dataproxy_client_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package flight - -import ( - "context" - "github.com/secretflow/kuscia/pkg/datamesh/config" - "testing" - - "github.com/apache/arrow/go/v13/arrow/flight" - "github.com/stretchr/testify/assert" - - "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" -) - -type MockFlightServer struct { - flight.BaseFlightServer -} - -func (m *MockFlightServer) GetFlightInfo(ctx context.Context, request *flight.FlightDescriptor) (*flight.FlightInfo, - error) { - info := &flight.FlightInfo{ - Schema: nil, - FlightDescriptor: nil, - Endpoint: []*flight.FlightEndpoint{ - { - Ticket: &flight.Ticket{ - Ticket: []byte("test-ticket"), - }, - Location: []*flight.Location{ - { - Uri: "dataproxy.DomainDataUnitTestNamespace.svc", - }, - }, - ExpirationTime: nil, - }, - }, - } - return info, nil -} - -func TestDataProxyClient(t *testing.T) { - server := flight.NewServerWithMiddleware(nil) - server.Init("localhost:0") - srv := &MockFlightServer{} - server.RegisterFlightService(srv) - - go server.Serve() - defer server.Shutdown() - - conf := &config.ExternalDataProxyConfig{ - Endpoint: server.Addr().String(), - } - client, err := NewDataProxyClient(conf) - assert.Nil(t, err) - - query := &datamesh.CommandDataMeshQuery{ - Query: &datamesh.CommandDomainDataQuery{ - DomaindataId: "test-data-id", - }, - } - info, err := client.GetFlightInfoDataMeshQuery(context.Background(), query) - assert.Nil(t, err) - ticket := string(info.Endpoint[0].Ticket.Ticket) - assert.Equal(t, ticket, "test-ticket") -} diff --git a/pkg/datamesh/flight/example/mock_dataproxy.go b/pkg/datamesh/flight/example/mock_dataproxy.go deleted file mode 100644 index 208821ed..00000000 --- a/pkg/datamesh/flight/example/mock_dataproxy.go +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//nolint:dupl -package example - -import ( - "bytes" - "context" - "fmt" - "os" - "strings" - - "github.com/apache/arrow/go/v13/arrow" - "github.com/apache/arrow/go/v13/arrow/array" - "github.com/apache/arrow/go/v13/arrow/csv" - "github.com/apache/arrow/go/v13/arrow/flight" - "github.com/apache/arrow/go/v13/arrow/ipc" - "github.com/apache/arrow/go/v13/arrow/memory" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - - "github.com/secretflow/kuscia/pkg/common" - "github.com/secretflow/kuscia/pkg/utils/nlog" - "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" -) - -type MockDataProxy struct { - Addr string - ticket2Query map[string]*datamesh.CommandDataMeshQuery - ticket2Update map[string]*datamesh.CommandDataMeshUpdate - flight.BaseFlightServer -} - -func NewMockDataProxy(addr string) *MockDataProxy { - return &MockDataProxy{ - Addr: addr, - ticket2Query: make(map[string]*datamesh.CommandDataMeshQuery), - ticket2Update: make(map[string]*datamesh.CommandDataMeshUpdate), - BaseFlightServer: flight.BaseFlightServer{}, - } -} - -func (m *MockDataProxy) Start() error { - server := flight.NewServerWithMiddleware(nil) - server.Init(m.Addr) - server.RegisterFlightService(m) - - nlog.Infof("start data proxy at:%s", m.Addr) - return server.Serve() -} - -func (m *MockDataProxy) GetFlightInfo(ctx context.Context, request *flight.FlightDescriptor) (*flight.FlightInfo, - error) { - var ( - anyCmd anypb.Any - msg proto.Message - err error - ticket *flight.Ticket - ) - - if err = proto.Unmarshal(request.Cmd, &anyCmd); err != nil { - return nil, status.Errorf(codes.InvalidArgument, "unable to parse command: %s", err.Error()) - } - - if msg, err = anyCmd.UnmarshalNew(); err != nil { - return nil, status.Errorf(codes.InvalidArgument, "could not unmarshal Any to a command type: %s", err.Error()) - } - - switch cmd := msg.(type) { - case *datamesh.CommandDataMeshQuery: - ticket, err = m.buildMockTicket(cmd) - case *datamesh.CommandDataMeshUpdate: - ticket, err = m.buildMockUpdateTicket(cmd) - default: - return nil, status.Errorf(codes.InvalidArgument, "invalid command") - } - - if err != nil { - return nil, err - } - - return m.buildMockFlightInfo(ticket) -} - -func (m *MockDataProxy) DoGet(tkt *flight.Ticket, fs flight.FlightService_DoGetServer) error { - var ( - anyCmd anypb.Any - msg proto.Message - err error - ) - if err = proto.Unmarshal(tkt.Ticket, &anyCmd); err != nil { - return status.Errorf(codes.Internal, "ticket unmarshall fail") - } - if msg, err = anyCmd.UnmarshalNew(); err != nil { - return status.Errorf(codes.Internal, "ticket unmarshallNew fail") - } - - switch cmd := msg.(type) { - case *datamesh.TicketDomainDataQuery: - return m.getDataByTicket(cmd.DomaindataHandle, fs) - default: - return status.Errorf(codes.Internal, "unknown ticket") - } -} - -func (m *MockDataProxy) DoPut(stream flight.FlightService_DoPutServer) error { - var ( - anyCmd anypb.Any - err error - query datamesh.TicketDomainDataQuery - rdr *flight.Reader - ) - - rdr, err = flight.NewRecordReader(stream) - if err != nil { - return status.Error(codes.Internal, err.Error()) - } - - // creating the reader should have gotten the first message which would - // have the schema, which should have a populated flight descriptor - desc := rdr.LatestFlightDescriptor() - if err = proto.Unmarshal(desc.Cmd, &anyCmd); err != nil { - return status.Errorf(codes.Internal, "ticket for Put unmarshall fail") - } - - if err = anyCmd.UnmarshalTo(&query); err != nil { - return status.Errorf(codes.Internal, "ticket for Put unmarshallNew fail") - } - - ticketID := query.DomaindataHandle - update, _ := m.ticket2Update[ticketID] - if update == nil { - return status.Errorf(codes.Internal, "ticket for put is invalid:%s", ticketID) - } - - if update.Datasource.Type != "localfs" { - return status.Errorf(codes.Internal, "mock dp just support put to localfs") - } - - path := strings.TrimRight(update.Datasource.Info.Localfs.Path, "/") + "/" + strings.TrimLeft(update.Domaindata. - RelativeUri, "/") - nlog.Infof("path of put request is :%s", path) - file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return status.Errorf(codes.Internal, "open file:%s fail, %v", path, err) - } - w := csv.NewWriter(file, rdr.Schema(), csv.WithComma(',')) - defer file.Close() - for rdr.Next() { - rec := rdr.Record() - rec.Retain() - w.Write(rec) - w.Flush() - if len(rdr.LatestAppMetadata()) > 0 { - stream.Send(&flight.PutResult{AppMetadata: rdr.LatestAppMetadata()}) - } - } - m.ticket2Update[ticketID] = nil - return nil -} - -func (m *MockDataProxy) getDataByTicket(ticketID string, fs flight.FlightService_DoGetServer) error { - //recs := Records[ticketID] - - query, ok := m.ticket2Query[ticketID] - if !ok || query == nil { - status.Errorf(codes.Internal, fmt.Sprintf("invalid ticket:%s", ticketID)) - } - - fields := make([]arrow.Field, 0) - for i, column := range query.Domaindata.Columns { - colType := common.Convert2ArrowColumnType(column.Type) - if colType == nil { - return status.Errorf(codes.Internal, "invalid column(%s)with type(%s)", column.Name, column.Type) - } - fields = append(fields, arrow.Field{ - Name: column.Name, - Type: colType, - Nullable: !column.NotNullable, - }) - nlog.Debugf("columns[%d].Nullable is :%v", i, !column.NotNullable) - } - schema := arrow.NewSchema(fields, nil) - - if query.Datasource.Type != "localfs" { - return status.Errorf(codes.Internal, "mock dp just support put to localfs") - } - path := query.Datasource.Info.Localfs.Path + query.Domaindata.RelativeUri - raw, err := os.ReadFile(path) - if err != nil { - return status.Errorf(codes.Internal, "file %s open fail:%v", path, err) - } - - reader := csv.NewReader(bytes.NewReader(raw), schema, csv.WithComma(','), csv.WithNullReader(true)) - recs := make([]arrow.Record, 0) - - for reader.Next() { - rec := reader.Record() - rec.Retain() - recs = append(recs, rec) - } - - nlog.Infof("read %d lines of %s", len(recs), path) - - if query.Query.ContentType == datamesh.ContentType_RAW { - dummySchema := arrow.NewSchema([]arrow.Field{ - { - Name: "dummy", - Type: arrow.BinaryTypes.Binary, - Nullable: false, - }, - }, nil) - w := flight.NewRecordWriter(fs, ipc.WithSchema(dummySchema)) - - mask := []bool{ - false, - } - var chunks [][]arrow.Array - defer func() { - for _, chunk := range chunks { - for _, col := range chunk { - col.Release() - } - } - w.Close() - }() - - mem := memory.NewGoAllocator() - for idx, r := range recs { - f := new(bytes.Buffer) - csvWriter := csv.NewWriter(f, schema) - csvWriter.Write(r) - csvWriter.Flush() - - chunk := []arrow.Array{ - arrayOf(mem, [][]byte{f.Bytes()}, mask), - } - chunks = append(chunks, chunk) - newR := array.NewRecord(dummySchema, chunk, -1) - w.WriteWithAppMetadata(newR, []byte(fmt.Sprintf("%d_%s", idx, ticketID)) /*metadata*/) - } - } else { - w := flight.NewRecordWriter(fs, ipc.WithSchema(recs[0].Schema())) - defer w.Close() - for idx, r := range recs { - w.WriteWithAppMetadata(r, []byte(fmt.Sprintf("%d_%s", idx, ticketID)) /*metadata*/) - } - } - - m.ticket2Query[ticketID] = nil - return nil -} - -func (m *MockDataProxy) buildMockFlightInfo(ticket *flight.Ticket) (*flight.FlightInfo, error) { - return &flight.FlightInfo{ - Schema: nil, - FlightDescriptor: &flight.FlightDescriptor{ - Type: flight.DescriptorCMD, - Cmd: []byte{}, - Path: []string{}, - }, - Endpoint: []*flight.FlightEndpoint{ - { - Ticket: ticket, - Location: []*flight.Location{ - { - Uri: fmt.Sprintf("grpc+tcp://%s", m.Addr), - }, - }, - ExpirationTime: nil, - }, - }, - }, nil -} - -func (m *MockDataProxy) buildMockTicket(query *datamesh.CommandDataMeshQuery) (*flight.Ticket, error) { - ticket := &datamesh.TicketDomainDataQuery{ - DomaindataHandle: query.Domaindata.DomaindataId, - } - - bakQuery, ok := proto.Clone(query).(*datamesh.CommandDataMeshQuery) - if !ok { - return nil, status.Error(codes.Internal, "clone CommandDataMeshQuery fail ") - } - - m.ticket2Query[query.Domaindata.DomaindataId] = bakQuery - - var ( - anyCmd anypb.Any - err error - body []byte - ) - - if err = anyCmd.MarshalFrom(ticket); err != nil { - return nil, status.Errorf(codes.Internal, fmt.Sprintf("%v: unable to marshal final response", err)) - } - if body, err = proto.Marshal(&anyCmd); err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - return &flight.Ticket{ - Ticket: body, - }, nil -} - -func (m *MockDataProxy) buildMockUpdateTicket(query *datamesh.CommandDataMeshUpdate) (*flight.Ticket, error) { - ticket := &datamesh.TicketDomainDataQuery{ - DomaindataHandle: query.Domaindata.DomaindataId, - } - - bakQuery, ok := proto.Clone(query).(*datamesh.CommandDataMeshUpdate) - if !ok { - return nil, status.Error(codes.Internal, "clone CommandDataMeshQuery fail ") - } - - m.ticket2Update[query.Domaindata.DomaindataId] = bakQuery - - var ( - anyCmd anypb.Any - err error - body []byte - ) - - if err = anyCmd.MarshalFrom(ticket); err != nil { - return nil, status.Errorf(codes.Internal, fmt.Sprintf("%v: unable to marshal final response", err)) - } - if body, err = proto.Marshal(&anyCmd); err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - return &flight.Ticket{ - Ticket: body, - }, nil -} diff --git a/pkg/datamesh/flight/metaserver.go b/pkg/datamesh/flight/metaserver.go deleted file mode 100644 index df39f322..00000000 --- a/pkg/datamesh/flight/metaserver.go +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package flight - -import ( - "context" - "fmt" - "strings" - - "github.com/apache/arrow/go/v13/arrow" - "github.com/apache/arrow/go/v13/arrow/flight" - "github.com/apache/arrow/go/v13/arrow/memory" - spb "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - - "github.com/secretflow/kuscia/pkg/common" - "github.com/secretflow/kuscia/pkg/datamesh/config" - "github.com/secretflow/kuscia/pkg/datamesh/service" - "github.com/secretflow/kuscia/pkg/utils/nlog" - "github.com/secretflow/kuscia/proto/api/v1alpha1" - "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" -) - -type MetaServer interface { - GetSchema(context.Context, *datamesh.CommandGetDomainDataSchema) (*flight.SchemaResult, error) - GetFlightInfoDomainDataQuery(context.Context, *datamesh.CommandDomainDataQuery) (*flight.FlightInfo, error) - GetFlightInfoDomainDataUpdate(context.Context, *datamesh.CommandDomainDataUpdate) (*flight.FlightInfo, error) - DoActionCreateDomainDataRequest(ctx context.Context, request *datamesh.ActionCreateDomainDataRequest) (*flight.Result, error) - DoActionQueryDomainDataRequest(ctx context.Context, request *datamesh.ActionQueryDomainDataRequest) (*flight.Result, error) - DoActionUpdateDomainDataRequest(ctx context.Context, request *datamesh.ActionUpdateDomainDataRequest) (*flight.Result, error) - DoActionDeleteDomainDataRequest(ctx context.Context, request *datamesh.ActionDeleteDomainDataRequest) (*flight.Result, error) - DoActionQueryDomainDataSourceRequest(ctx context.Context, request *datamesh.ActionQueryDomainDataSourceRequest) ( - *flight.Result, error) -} - -type DomainDataMetaServer struct { - domainDataService service.IDomainDataService - domainDataSourceService service.IDomainDataSourceService - dataServer DataServer - datamesh.UnimplementedDomainDataServiceServer - datamesh.UnimplementedDomainDataSourceServiceServer -} - -func NewMetaServer(domainDataService service.IDomainDataService, dataSourceService service.IDomainDataSourceService, - config *config.ExternalDataProxyConfig) (*DomainDataMetaServer, error) { - dataServer, err := NewDataProxyClient(config) - if err != nil { - return nil, err - } - return &DomainDataMetaServer{ - domainDataService: domainDataService, - domainDataSourceService: dataSourceService, - dataServer: dataServer, - }, nil -} - -func (s *DomainDataMetaServer) GetSchema(ctx context.Context, query *datamesh.CommandGetDomainDataSchema) (*flight.SchemaResult, error) { - domainDataResp, err := s.getDomainData(ctx, query.DomaindataId) - if err != nil { - return nil, err - } - domainData := domainDataResp.Data - - var fields []arrow.Field - for _, column := range domainData.Columns { - colType := common.Convert2ArrowColumnType(column.Type) - if colType == nil { - return nil, buildGrpcErrorf(nil, codes.FailedPrecondition, "invalid column(%s)with type(%s)", column.Name, - column.Type) - } - fields = append(fields, arrow.Field{ - Name: column.Name, - Type: colType, - Nullable: !column.NotNullable, - }) - } - - metadata := arrow.NewMetadata([]string{"type"}, []string{domainData.Type}) - result := &flight.SchemaResult{ - Schema: flight.SerializeSchema(arrow.NewSchema(fields, &metadata), memory.DefaultAllocator), - } - return result, nil -} - -func (s *DomainDataMetaServer) GetFlightInfoDomainDataQuery(ctx context.Context, - query *datamesh.CommandDomainDataQuery) (*flight.FlightInfo, error) { - domainDataResp, err := s.getDomainData(ctx, query.DomaindataId) - if err != nil { - return nil, err - } - - datasource, err := s.getDataSource(ctx, domainDataResp.Data.GetDatasourceId()) - if err != nil { - return nil, err - } - - // adjust ContentType - if strings.ToLower(domainDataResp.Data.Type) != "table" { - query.ContentType = datamesh.ContentType_RAW - } - - if err := service.CheckColType(domainDataResp.Data.Columns, datasource.Type); err != nil { - return nil, buildGrpcErrorf(nil, codes.FailedPrecondition, err.Error()) - } - - dpQuery := &datamesh.CommandDataMeshQuery{ - Query: query, - Domaindata: domainDataResp.Data, - Datasource: datasource, - } - - return s.dataServer.GetFlightInfoDataMeshQuery(ctx, dpQuery) -} - -func (s *DomainDataMetaServer) GetFlightInfoDomainDataUpdate(ctx context.Context, - query *datamesh.CommandDomainDataUpdate) (*flight.FlightInfo, error) { - var ( - domainData *datamesh.DomainData - datasource *datamesh.DomainDataSource - err error - - domainDataResp *datamesh.QueryDomainDataResponse - ) - - domainDataID := query.DomaindataId - - if query.GetDomaindataRequest() != nil { - if _, err = s.createDomainData(ctx, query.GetDomaindataRequest()); err != nil { - return nil, err - } - domainDataID = query.GetDomaindataRequest().DomaindataId - } - - if domainDataResp, err = s.getDomainData(ctx, domainDataID); err != nil { - return nil, err - } - domainData = domainDataResp.Data - - if datasource, err = s.getDataSource(ctx, domainData.GetDatasourceId()); err != nil { - return nil, err - } - - if err := service.CheckColType(domainDataResp.Data.Columns, datasource.Type); err != nil { - return nil, buildGrpcErrorf(nil, codes.FailedPrecondition, err.Error()) - } - - // adjust ContentType - if strings.ToLower(domainDataResp.Data.Type) != "table" { - query.ContentType = datamesh.ContentType_RAW - } - - dpUpdate := &datamesh.CommandDataMeshUpdate{ - Update: query, - Domaindata: domainData, - Datasource: datasource, - } - - return s.dataServer.GetFlightInfoDataMeshUpdate(ctx, dpUpdate) -} - -func (s *DomainDataMetaServer) DoActionCreateDomainDataRequest(ctx context.Context, request *datamesh.ActionCreateDomainDataRequest) (*flight. - Result, error) { - domainDataResp, err := s.createDomainData(ctx, request.Request) - if err != nil { - return nil, err - } - - actionResp := &datamesh.ActionCreateDomainDataResponse{ - Response: domainDataResp, - } - return packActionResult(actionResp) -} - -func (s *DomainDataMetaServer) DoActionQueryDomainDataRequest(ctx context.Context, - request *datamesh.ActionQueryDomainDataRequest) (*flight.Result, error) { - domainDataResp, err := s.getDomainData(ctx, request.Request.DomaindataId) - if err != nil { - return nil, err - } - - actionResp := &datamesh.ActionQueryDomainDataResponse{ - Response: domainDataResp, - } - return packActionResult(actionResp) -} - -func (s *DomainDataMetaServer) DoActionUpdateDomainDataRequest(ctx context.Context, - request *datamesh.ActionUpdateDomainDataRequest) (*flight.Result, error) { - domainDataResp := s.domainDataService.UpdateDomainData(ctx, request.Request) - if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { - var appStatus *v1alpha1.Status - if domainDataResp.GetStatus() != nil { - appStatus = domainDataResp.GetStatus() - } - return nil, buildGrpcErrorf(appStatus, codes.Internal, "update domain data with id(%s) fail", request.Request.DomaindataId) - } - - actionResp := &datamesh.ActionUpdateDomainDataResponse{ - Response: domainDataResp, - } - return packActionResult(actionResp) -} - -func (s *DomainDataMetaServer) DoActionDeleteDomainDataRequest(ctx context.Context, - request *datamesh.ActionDeleteDomainDataRequest) (*flight.Result, error) { - domainDataResp := s.domainDataService.DeleteDomainData(ctx, request.Request) - if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { - var appStatus *v1alpha1.Status - if domainDataResp.GetStatus() != nil { - appStatus = domainDataResp.GetStatus() - } - return nil, buildGrpcErrorf(appStatus, codes.Internal, "delete domain data with id(%s) fail", - request.Request.DomaindataId) - } - - actionResp := &datamesh.ActionDeleteDomainDataResponse{ - Response: domainDataResp, - } - return packActionResult(actionResp) -} - -func (s *DomainDataMetaServer) DoActionQueryDomainDataSourceRequest(ctx context.Context, - request *datamesh.ActionQueryDomainDataSourceRequest) (*flight.Result, error) { - domainSourceResp := s.domainDataSourceService.QueryDomainDataSource(ctx, request.Request) - if domainSourceResp == nil || domainSourceResp.GetStatus() == nil || domainSourceResp.GetStatus().GetCode() != 0 { - var appStatus *v1alpha1.Status - if domainSourceResp.GetStatus() != nil { - appStatus = domainSourceResp.GetStatus() - } - return nil, buildGrpcErrorf(appStatus, codes.Internal, "query datasource with id(%s) fail", - request.Request.DatasourceId) - } - - actionResp := &datamesh.ActionQueryDomainDataSourceResponse{ - Response: domainSourceResp, - } - return packActionResult(actionResp) -} - -func (s *DomainDataMetaServer) getDomainData(ctx context.Context, id string) (*datamesh.QueryDomainDataResponse, error) { - domainDataReq := &datamesh.QueryDomainDataRequest{ - DomaindataId: id, - } - - domainDataResp := s.domainDataService.QueryDomainData(ctx, domainDataReq) - if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { - var appStatus *v1alpha1.Status - if domainDataResp.GetStatus() != nil { - appStatus = domainDataResp.GetStatus() - } - return nil, buildGrpcErrorf(appStatus, codes.Internal, "query domain data by id(%s) fail", id) - } - return domainDataResp, nil -} - -func (s *DomainDataMetaServer) createDomainData(ctx context.Context, - domainDataReq *datamesh.CreateDomainDataRequest) (*datamesh.CreateDomainDataResponse, error) { - domainDataResp := s.domainDataService.CreateDomainData(ctx, domainDataReq) - if domainDataResp == nil || domainDataResp.GetStatus() == nil || domainDataResp.GetStatus().GetCode() != 0 { - var appStatus *v1alpha1.Status - if domainDataResp.GetStatus() != nil { - appStatus = domainDataResp.GetStatus() - } - return nil, buildGrpcErrorf(appStatus, codes.Internal, "create domain data with id(%s) fail", - domainDataReq.DomaindataId) - } - return domainDataResp, nil -} - -func (s *DomainDataMetaServer) getDataSource(ctx context.Context, id string) (*datamesh.DomainDataSource, error) { - datasourceReq := &datamesh.QueryDomainDataSourceRequest{ - DatasourceId: id, - } - - datasourceResp := s.domainDataSourceService.QueryDomainDataSource(ctx, datasourceReq) - if datasourceResp == nil || datasourceResp.GetStatus() == nil || datasourceResp.GetStatus().GetCode() != 0 { - var appStatus *v1alpha1.Status - if datasourceResp.GetStatus() != nil { - appStatus = datasourceResp.GetStatus() - } - return nil, buildGrpcErrorf(appStatus, codes.Internal, "query data source by id(%s) fail", datasourceReq.DatasourceId) - } - return datasourceResp.GetData(), nil -} - -func buildGrpcErrorf(appStatus *v1alpha1.Status, code codes.Code, format string, a ...interface{}) error { - if appStatus == nil { - nlog.Warnf(format, a...) - return status.Errorf(code, format, a...) - } - - s := &spb.Status{ - Code: int32(code), - } - if len(appStatus.Message) > 0 { - s.Message = appStatus.Message - } else { - s.Message = fmt.Sprintf(format, a...) - } - nlog.Warnf("%s", s.Message) - - if detail, err := anypb.New(appStatus); err != nil { - s.Details = []*anypb.Any{ - detail, - } - } - return status.ErrorProto(s) -} - -func packActionResult(msg proto.Message) (*flight.Result, error) { - var ( - err error - ) - - ret := &flight.Result{} - if ret.Body, err = proto.Marshal(msg); err != nil { - nlog.Warnf("unable to marshal msg to flight.Result.Body: %v", err) - return nil, status.Errorf(codes.Internal, fmt.Sprintf("Unable to marshal final response: %v", err)) - } - return ret, nil -} diff --git a/pkg/datamesh/flight/metaserver_test.go b/pkg/datamesh/flight/metaserver_test.go deleted file mode 100644 index eb0fc46f..00000000 --- a/pkg/datamesh/flight/metaserver_test.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package flight - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "fmt" - "testing" - - "github.com/apache/arrow/go/v13/arrow" - "github.com/apache/arrow/go/v13/arrow/flight" - "github.com/apache/arrow/go/v13/arrow/memory" - "github.com/stretchr/testify/assert" - - "github.com/secretflow/kuscia/pkg/common" - cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" - kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" - "github.com/secretflow/kuscia/pkg/datamesh/config" - "github.com/secretflow/kuscia/pkg/datamesh/service" - "github.com/secretflow/kuscia/pkg/secretbackend" - _ "github.com/secretflow/kuscia/pkg/secretbackend/mem" - "github.com/secretflow/kuscia/proto/api/v1alpha1" - "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" -) - -var ( - dsID = common.DefaultDataSourceID -) - -type MockDataServer struct { -} - -func mockFlightInfo(domainDataID string) *flight.FlightInfo { - info := &flight.FlightInfo{ - Schema: nil, - FlightDescriptor: nil, - Endpoint: []*flight.FlightEndpoint{ - { - Ticket: &flight.Ticket{ - Ticket: []byte(fmt.Sprintf("%s-handler", domainDataID)), - }, - Location: []*flight.Location{ - { - Uri: "dataproxy.DomainDataUnitTestNamespace.svc", - }, - }, - ExpirationTime: nil, - }, - }, - } - return info -} - -func (m *MockDataServer) GetFlightInfoDataMeshQuery(ctx context.Context, query *datamesh.CommandDataMeshQuery) (*flight.FlightInfo, error) { - return mockFlightInfo(query.Domaindata.DomaindataId), nil -} - -func (m *MockDataServer) GetFlightInfoDataMeshUpdate(ctx context.Context, query *datamesh.CommandDataMeshUpdate) (*flight.FlightInfo, error) { - return mockFlightInfo("test"), nil -} - -func createTestDomainDataSource(t *testing.T, datasourceService service.IDomainDataSourceService) string { - err := datasourceService.CreateDefaultDomainDataSource(context.Background()) - assert.Nil(t, err) - - return common.DefaultDataSourceID -} - -func createDomainData(domainDataService service.IDomainDataService, - datasourceId string, t *testing.T) *datamesh.CreateDomainDataResponse { - attr := make(map[string]string) - attr["rows"] = "100" - col := make([]*v1alpha1.DataColumn, 2) - col[0] = &v1alpha1.DataColumn{Name: "id", Type: "int32"} - col[1] = &v1alpha1.DataColumn{Name: "date", Type: "string"} - res := domainDataService.CreateDomainData(context.Background(), &datamesh.CreateDomainDataRequest{ - Header: nil, - DomaindataId: "", - Name: "test", - Type: "table", - RelativeUri: "a/b/c.csv", - DatasourceId: datasourceId, - Attributes: attr, - Partition: &v1alpha1.Partition{ - Type: "path", - Fields: col[1:], - }, - Columns: col, - }) - assert.NotNil(t, res) - return res -} - -func TestGetSchema(t *testing.T) { - key, err := rsa.GenerateKey(rand.Reader, 2048) - assert.NoError(t, err) - conf := &config.DataMeshConfig{ - KusciaClient: kusciafake.NewSimpleClientset(), - KubeNamespace: "DomainDataUnitTestNamespace", - DomainKey: key, - } - datasourceService := makeDomainDataSourceService(t, conf) - dsID = createTestDomainDataSource(t, datasourceService) - domainDataService := service.NewDomainDataService(conf) - res := createDomainData(domainDataService, dsID, t) - - metaSrv := &DomainDataMetaServer{ - domainDataService: domainDataService, - domainDataSourceService: nil, - dataServer: nil, - } - - query := &datamesh.CommandGetDomainDataSchema{ - DomaindataId: res.Data.DomaindataId, - } - - schema, err := metaSrv.GetSchema(context.Background(), query) - assert.Nil(t, err) - - arrowSchema, err := flight.DeserializeSchema(schema.GetSchema(), memory.DefaultAllocator) - assert.Nil(t, err) - assert.Equal(t, arrowSchema.Field(0).Type, arrow.PrimitiveTypes.Int32) - assert.Equal(t, arrowSchema.Field(1).Type, arrow.BinaryTypes.String) -} - -func TestCommandDomainDataQuery(t *testing.T) { - key, err := rsa.GenerateKey(rand.Reader, 2048) - assert.NoError(t, err) - conf := &config.DataMeshConfig{ - KusciaClient: kusciafake.NewSimpleClientset(), - KubeNamespace: "DomainDataUnitTestNamespace", - DomainKey: key, - } - datasourceService := makeDomainDataSourceService(t, conf) - dsID = createTestDomainDataSource(t, datasourceService) - domainDataService := service.NewDomainDataService(conf) - domainDataResp := createDomainData(domainDataService, dsID, t) - - metaSrv := &DomainDataMetaServer{ - domainDataService: domainDataService, - domainDataSourceService: datasourceService, - dataServer: &MockDataServer{}, - } - - query := &datamesh.CommandDomainDataQuery{ - DomaindataId: domainDataResp.Data.DomaindataId, - } - - info, err := metaSrv.GetFlightInfoDomainDataQuery(context.Background(), query) - assert.Nil(t, err) - ticket := string(info.Endpoint[0].Ticket.Ticket) - - expected := fmt.Sprintf("%s-handler", domainDataResp.Data.DomaindataId) - assert.Equal(t, ticket, expected) -} - -func TestCommandDomainDataUpdate(t *testing.T) { - key, err := rsa.GenerateKey(rand.Reader, 2048) - assert.NoError(t, err) - conf := &config.DataMeshConfig{ - KusciaClient: kusciafake.NewSimpleClientset(), - KubeNamespace: "DomainDataUnitTestNamespace", - DomainKey: key, - } - datasourceService := makeDomainDataSourceService(t, conf) - dsID = createTestDomainDataSource(t, datasourceService) - - domainDataService := service.NewDomainDataService(conf) - - metaSrv := &DomainDataMetaServer{ - domainDataService: domainDataService, - domainDataSourceService: datasourceService, - dataServer: &MockDataServer{}, - } - - col := make([]*v1alpha1.DataColumn, 2) - col[0] = &v1alpha1.DataColumn{Name: "id", Type: "int32"} - col[1] = &v1alpha1.DataColumn{Name: "date", Type: "string"} - domainDataReq := &datamesh.CreateDomainDataRequest{ - Header: nil, - DomaindataId: "", - Name: "test", - Type: "table", - RelativeUri: "a/b/c.csv", - DatasourceId: dsID, - Partition: &v1alpha1.Partition{ - Type: "path", - Fields: col[1:], - }, - Columns: col, - } - - update := &datamesh.CommandDomainDataUpdate{ - DomaindataRequest: domainDataReq, - } - - info, err := metaSrv.GetFlightInfoDomainDataUpdate(context.Background(), update) - assert.Nil(t, err) - ticket := string(info.Endpoint[0].Ticket.Ticket) - assert.True(t, len(ticket) > 0) -} - -func makeDomainDataSourceService(t *testing.T, conf *config.DataMeshConfig) service.IDomainDataSourceService { - return service.NewDomainDataSourceService(conf, makeMemConfigurationService(t)) -} - -func makeMemConfigurationService(t *testing.T) cmservice.IConfigurationService { - backend, err := secretbackend.NewSecretBackendWith("mem", map[string]any{}) - assert.Nil(t, err) - assert.NotNil(t, backend) - configurationService, err := cmservice.NewConfigurationService( - backend, false, - ) - assert.Nil(t, err) - assert.NotNil(t, configurationService) - return configurationService -} diff --git a/pkg/datamesh/flight/metautils.go b/pkg/datamesh/flight/metautils.go deleted file mode 100644 index 24e54e91..00000000 --- a/pkg/datamesh/flight/metautils.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package flight - -import ( - "github.com/apache/arrow/go/v13/arrow/flight" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - - "github.com/secretflow/kuscia/pkg/utils/nlog" - "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" -) - -func DescForCommand(cmd proto.Message) (*flight.FlightDescriptor, error) { - var any anypb.Any - if err := any.MarshalFrom(cmd); err != nil { - return nil, err - } - - data, err := proto.Marshal(&any) - if err != nil { - return nil, err - } - return &flight.FlightDescriptor{ - Type: flight.DescriptorCMD, - Cmd: data, - }, nil -} - -func GetTicketDomainDataQuery(in *flight.Ticket) (*datamesh.TicketDomainDataQuery, error) { - var any anypb.Any - if err := proto.Unmarshal(in.Ticket, &any); err != nil { - nlog.Warnf("Unmarshal Ticket to any fail: %v", err) - return nil, err - } - - var out datamesh.TicketDomainDataQuery - if err := any.UnmarshalTo(&out); err != nil { - return nil, err - } - return &out, nil -} diff --git a/pkg/datamesh/handler/grpchandler/flight_handler.go b/pkg/datamesh/handler/grpchandler/flight_handler.go deleted file mode 100644 index 0fd29957..00000000 --- a/pkg/datamesh/handler/grpchandler/flight_handler.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grpchandler - -import ( - "context" - - "github.com/apache/arrow/go/v13/arrow/flight" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - - flight2 "github.com/secretflow/kuscia/pkg/datamesh/flight" - "github.com/secretflow/kuscia/pkg/utils/nlog" - "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" -) - -type FlightMetaHandler struct { - flight.BaseFlightServer - srv flight2.MetaServer -} - -func NewFlightMetaHandler(srv flight2.MetaServer) *FlightMetaHandler { - return &FlightMetaHandler{ - srv: srv, - } -} - -func (f *FlightMetaHandler) GetSchema(ctx context.Context, request *flight.FlightDescriptor) (*flight.SchemaResult, - error) { - var ( - anyCmd anypb.Any - msg proto.Message - err error - ) - - if err = proto.Unmarshal(request.Cmd, &anyCmd); err != nil { - return nil, status.Errorf(codes.InvalidArgument, "unable to parse command: %s", err.Error()) - } - - if msg, err = anyCmd.UnmarshalNew(); err != nil { - return nil, status.Errorf(codes.InvalidArgument, "could not unmarshal Any to a command type: %s", err.Error()) - } - - switch cmd := msg.(type) { - case *datamesh.CommandGetDomainDataSchema: - return f.srv.GetSchema(ctx, cmd) - } - - return nil, status.Error(codes.InvalidArgument, "request command is invalid") -} - -func (f *FlightMetaHandler) GetFlightInfo(ctx context.Context, request *flight.FlightDescriptor) (*flight.FlightInfo, - error) { - var ( - anyCmd anypb.Any - msg proto.Message - err error - flightInfo *flight.FlightInfo - ) - - if err = proto.Unmarshal(request.Cmd, &anyCmd); err != nil { - nlog.Warnf("Unable to parse FlightDescriptor.Cmd to Any") - return nil, status.Errorf(codes.InvalidArgument, "unable to parse command: %v", err) - } - - if msg, err = anyCmd.UnmarshalNew(); err != nil { - nlog.Warnf("FlightDescriptor.Cmd UnmarshalNew fail: %v", err) - return nil, status.Errorf(codes.InvalidArgument, "could not unmarshal Any to a command type: %s", err.Error()) - } - - switch cmd := msg.(type) { - case *datamesh.CommandDomainDataQuery: - flightInfo, err = f.srv.GetFlightInfoDomainDataQuery(ctx, cmd) - case *datamesh.CommandDomainDataUpdate: - flightInfo, err = f.srv.GetFlightInfoDomainDataUpdate(ctx, cmd) - default: - err = status.Error(codes.InvalidArgument, "FlightDescriptor.Cmd of GetFlightInfo Request is invalid") - } - - if err != nil { - nlog.Warnf("GetFlightInfo fail: %v", err) - } - return flightInfo, err -} - -func (f *FlightMetaHandler) DoAction(action *flight.Action, stream flight.FlightService_DoActionServer) error { - var ( - result *flight.Result - err error - ) - - buildUnmarshalError := func() error { - nlog.Warnf("Action Request body can not deserialize to %s, body size:%d", action.Type, len(action.Body)) - return status.Errorf(codes.InvalidArgument, "request action body can not deserialize to %s", action.Type) - } - - switch action.Type { - case "ActionCreateDomainDataRequest": - var ( - request datamesh.ActionCreateDomainDataRequest - ) - if err = proto.Unmarshal(action.Body, &request); err != nil { - return buildUnmarshalError() - } - if result, err = f.srv.DoActionCreateDomainDataRequest(context.Background(), &request); err != nil { - return err - } - case "ActionQueryDomainDataRequest": - var ( - request datamesh.ActionQueryDomainDataRequest - ) - if err = proto.Unmarshal(action.Body, &request); err != nil { - return buildUnmarshalError() - } - result, err = f.srv.DoActionQueryDomainDataRequest(context.Background(), &request) - case "ActionUpdateDomainDataRequest": - var ( - request datamesh.ActionUpdateDomainDataRequest - ) - if err = proto.Unmarshal(action.Body, &request); err != nil { - return buildUnmarshalError() - } - result, err = f.srv.DoActionUpdateDomainDataRequest(context.Background(), &request) - case "ActionDeleteDomainDataRequest": - var ( - request datamesh.ActionDeleteDomainDataRequest - ) - if err = proto.Unmarshal(action.Body, &request); err != nil { - return buildUnmarshalError() - } - result, err = f.srv.DoActionDeleteDomainDataRequest(context.Background(), &request) - case "ActionQueryDomainDataSourceRequest": - var ( - request datamesh.ActionQueryDomainDataSourceRequest - ) - if err = proto.Unmarshal(action.Body, &request); err != nil { - return buildUnmarshalError() - } - result, err = f.srv.DoActionQueryDomainDataSourceRequest(context.Background(), &request) - default: - err = status.Errorf(codes.InvalidArgument, "unsupported action type: %s", action.Type) - } - - if err != nil { - nlog.Warnf("DoAction %s fail: %v", action.Type, err) - return err - } - - return stream.Send(result) -} diff --git a/pkg/datamesh/service/domaindata.go b/pkg/datamesh/service/domaindata.go index 5e8d1741..269027bb 100644 --- a/pkg/datamesh/service/domaindata.go +++ b/pkg/datamesh/service/domaindata.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -26,12 +26,12 @@ import ( "github.com/secretflow/kuscia/pkg/common" "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" "github.com/secretflow/kuscia/pkg/datamesh/config" - "github.com/secretflow/kuscia/pkg/datamesh/errorcode" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/resources" "github.com/secretflow/kuscia/pkg/web/utils" pbv1alpha1 "github.com/secretflow/kuscia/proto/api/v1alpha1" "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) type IDomainDataService interface { @@ -57,7 +57,7 @@ func (s domainDataService) CreateDomainData(ctx context.Context, request *datame // do k8s validate if err := resources.ValidateK8sName(request.DomaindataId, "domaindata_id"); err != nil { return &datamesh.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrRequestInvalidate, err.Error()), } } domainData, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDatas(s.conf.KubeNamespace).Get(ctx, request.DomaindataId, metav1.GetOptions{}) @@ -75,7 +75,7 @@ func (s domainDataService) CreateDomainData(ctx context.Context, request *datame datasource, err := s.checkDataSource(ctx, request.DatasourceId, request.Columns) if err != nil { return &datamesh.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrGetDomainDataSourceFromKubeFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrGetDomainDataSourceFromKubeFailed, err.Error()), } } if !isFSDataSource(datasource.Spec.Type) { @@ -116,7 +116,7 @@ func (s domainDataService) CreateDomainData(ctx context.Context, request *datame if err != nil { nlog.Errorf("CreateDomainData failed, error: %s", err.Error()) return &datamesh.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrCreateDomainData, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrCreateDomainData, err.Error()), } } return &datamesh.CreateDomainDataResponse{ @@ -133,7 +133,7 @@ func (s domainDataService) QueryDomainData(ctx context.Context, request *datames if err != nil { nlog.Errorf("QueryDomainData failed, error: %s", err.Error()) return &datamesh.QueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrQueryDomainData, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrQueryDomainData, err.Error()), } } // build domain response @@ -161,7 +161,7 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *datame if err != nil { nlog.Errorf("UpdateDomainData failed, error: %s", err.Error()) return &datamesh.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrGetDomainDataFromKubeFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrGetDomainDataFromKubeFailed, err.Error()), } } @@ -174,7 +174,7 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *datame if err != nil { nlog.Errorf("Query DataSource %s of DomainData %s fail: %v", request.DatasourceId, request.DomaindataId, err) return &datamesh.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrGetDomainDataSourceFromKubeFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrGetDomainDataSourceFromKubeFailed, err.Error()), } } if !isFSDataSource(datasource.Spec.Type) { @@ -211,12 +211,12 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *datame }, } // merge modifiedDomainData to originalDomainData - patchBytes, originalBytes, modifiedBytes, err := common.MergeDomainData(originalDomainData, modifiedDomainData) + patchBytes, originalBytes, modifiedBytes, err := MergeDomainData(originalDomainData, modifiedDomainData) if err != nil { nlog.Errorf("Merge DomainData failed, request: %+v,error: %s.", request, err.Error()) return &datamesh.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrMergeDomainDataFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrMergeDomainDataFailed, err.Error()), } } nlog.Debugf("Update DomainData request: %+v, patchBytes: %s, originalDomainData: %s, modifiedDomainData: %s", @@ -228,7 +228,7 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *datame nlog.Debugf("Patch DomainData failed, request: %+v, patchBytes: %s, originalDomainData: %s, modifiedDomainData: %s, error: %s", request, patchBytes, originalBytes, modifiedBytes, err.Error()) return &datamesh.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrPatchDomainDataFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrPatchDomainDataFailed, err.Error()), } } // construct the response @@ -239,13 +239,13 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *datame func (s domainDataService) DeleteDomainData(ctx context.Context, request *datamesh.DeleteDomainDataRequest) *datamesh.DeleteDomainDataResponse { // record the delete operation - nlog.Warnf("Delete domainDataId %s", request.DomaindataId) + nlog.Warnf("Delete domainDataID %s", request.DomaindataId) // delete kuscia domainData err := s.conf.KusciaClient.KusciaV1alpha1().DomainDatas(s.conf.KubeNamespace).Delete(ctx, request.DomaindataId, metav1.DeleteOptions{}) if err != nil { nlog.Errorf("Delete domainData: %s failed, detail: %s", request.DomaindataId, err.Error()) return &datamesh.DeleteDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrDeleteDomainDataFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrDeleteDomainDataFailed, err.Error()), } } return &datamesh.DeleteDomainDataResponse{ diff --git a/pkg/datamesh/service/domaindatagrant.go b/pkg/datamesh/service/domaindatagrant.go index 8552ed24..4f6b6c67 100644 --- a/pkg/datamesh/service/domaindatagrant.go +++ b/pkg/datamesh/service/domaindatagrant.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -36,6 +36,7 @@ import ( "github.com/secretflow/kuscia/pkg/utils/resources" "github.com/secretflow/kuscia/pkg/web/utils" "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) type IDomainDataGrantService interface { @@ -59,26 +60,26 @@ func (s *domainDataGrantService) CreateDomainDataGrant(ctx context.Context, requ if validateErr := validateCreateDomainDataGrantRequest(request); validateErr != nil { return &datamesh.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, validateErr.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, validateErr.Error()), } } if request.GrantDomain == s.conf.KubeNamespace { return &datamesh.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "grantdomain cant be self"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "grantdomain cant be self"), } } dd, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDatas(s.conf.KubeNamespace).Get(ctx, request.DomaindataId, metav1.GetOptions{}) if err != nil { return &datamesh.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, fmt.Sprintf("domaindata [%s] not exists", request.DomaindataId)), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, fmt.Sprintf("domaindata [%s] not exists", request.DomaindataId)), } } if request.DomaindatagrantId != "" { _, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDataGrants(s.conf.KubeNamespace).Get(ctx, request.DomaindatagrantId, metav1.GetOptions{}) if err == nil { return &datamesh.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrCreateDomainDataGrant), fmt.Sprintf("CreateDomainDataGrant failed, because domaindatagrant %s is exist", request.DomaindatagrantId)), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_DataMeshErrCreateDomainDataGrant), fmt.Sprintf("CreateDomainDataGrant failed, because domaindatagrant %s is exist", request.DomaindatagrantId)), } } } @@ -96,7 +97,7 @@ func (s *domainDataGrantService) CreateDomainDataGrant(ctx context.Context, requ if err != nil { nlog.Errorf("CreateDomainDataGrant failed, error:%s", err.Error()) return &datamesh.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrCreateDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_DataMeshErrCreateDomainDataGrant), err.Error()), } } @@ -104,7 +105,7 @@ func (s *domainDataGrantService) CreateDomainDataGrant(ctx context.Context, requ if err != nil { nlog.Errorf("CreateDomainDataGrant failed, error:%s", err.Error()) return &datamesh.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrCreateDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_DataMeshErrCreateDomainDataGrant), err.Error()), } } nlog.Infof("Create DomainDataGrant %s/%s", s.conf.KubeNamespace, dg.Name) @@ -119,14 +120,14 @@ func (s *domainDataGrantService) CreateDomainDataGrant(ctx context.Context, requ func (s *domainDataGrantService) QueryDomainDataGrant(ctx context.Context, request *datamesh.QueryDomainDataGrantRequest) *datamesh.QueryDomainDataGrantResponse { if request.DomaindatagrantId == "" { return &datamesh.QueryDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "domaindatagrantid cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "domaindatagrantid cant be null"), } } dg, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDataGrants(s.conf.KubeNamespace).Get(ctx, request.DomaindatagrantId, metav1.GetOptions{}) if err != nil { nlog.Errorf("Query DomainDataGrant failed, error:%s", err.Error()) return &datamesh.QueryDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrQueryDomainDataGrant, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrQueryDomainDataGrant, err.Error()), } } @@ -141,38 +142,38 @@ func (s *domainDataGrantService) QueryDomainDataGrant(ctx context.Context, reque func (s *domainDataGrantService) UpdateDomainDataGrant(ctx context.Context, request *datamesh.UpdateDomainDataGrantRequest) *datamesh.UpdateDomainDataGrantResponse { if request.DomaindatagrantId == "" { return &datamesh.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "domaindatagrantid cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "domaindatagrantid cant be null"), } } if request.DomaindataId == "" { return &datamesh.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "domaindata cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "domaindata cant be null"), } } _, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDatas(s.conf.KubeNamespace).Get(ctx, request.DomaindataId, metav1.GetOptions{}) if err != nil { return &datamesh.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "domaindata cant be found"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "domaindata cant be found"), } } if request.GrantDomain == "" { return &datamesh.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "grantdomain cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "grantdomain cant be null"), } } if request.GrantDomain == s.conf.KubeNamespace { return &datamesh.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "grantdomain cant be self"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "grantdomain cant be self"), } } dg, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDataGrants(s.conf.KubeNamespace).Get(ctx, request.DomaindatagrantId, metav1.GetOptions{}) if err != nil { nlog.Errorf("Get DomainDataGrant failed, error:%s", err.Error()) return &datamesh.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrUpdateDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_DataMeshErrUpdateDomainDataGrant), err.Error()), } } @@ -188,7 +189,7 @@ func (s *domainDataGrantService) UpdateDomainDataGrant(ctx context.Context, requ if err != nil { nlog.Errorf("Update DomainDataGrant failed, error:%s", err.Error()) return &datamesh.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrUpdateDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_DataMeshErrUpdateDomainDataGrant), err.Error()), } } nlog.Infof("Update DomainDataGrant %s/%s", s.conf.KubeNamespace, request.DomaindatagrantId) @@ -200,14 +201,14 @@ func (s *domainDataGrantService) UpdateDomainDataGrant(ctx context.Context, requ func (s *domainDataGrantService) DeleteDomainDataGrant(ctx context.Context, request *datamesh.DeleteDomainDataGrantRequest) *datamesh.DeleteDomainDataGrantResponse { if request.DomaindatagrantId == "" { return &datamesh.DeleteDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestInvalidate, "domaindatagrantid cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrRequestInvalidate, "domaindatagrantid cant be null"), } } nlog.Warnf("Delete domainDataGrantId %s/%s", s.conf.KubeNamespace, request.DomaindatagrantId) err := s.conf.KusciaClient.KusciaV1alpha1().DomainDataGrants(s.conf.KubeNamespace).Delete(ctx, request.DomaindatagrantId, metav1.DeleteOptions{}) if err != nil { return &datamesh.DeleteDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrDeleteDomainDataGrant, fmt.Sprintf("Delete domainDataGrantId:%s failed, detail:%s", request.DomaindatagrantId, err.Error())), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_DataMeshErrDeleteDomainDataGrant, fmt.Sprintf("Delete domainDataGrantId:%s failed, detail:%s", request.DomaindatagrantId, err.Error())), } } return &datamesh.DeleteDomainDataGrantResponse{ diff --git a/pkg/datamesh/service/domaindatasource.go b/pkg/datamesh/service/domaindatasource.go index ed86a2ce..f830d3ba 100644 --- a/pkg/datamesh/service/domaindatasource.go +++ b/pkg/datamesh/service/domaindatasource.go @@ -28,12 +28,12 @@ import ( cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" "github.com/secretflow/kuscia/pkg/datamesh/config" - "github.com/secretflow/kuscia/pkg/datamesh/errorcode" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/tls" "github.com/secretflow/kuscia/pkg/web/utils" "github.com/secretflow/kuscia/proto/api/v1alpha1/confmanager" "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) const ( @@ -137,7 +137,7 @@ func (s domainDataSourceService) QueryDomainDataSource(ctx context.Context, requ if err != nil { nlog.Errorf("QueryDomainDataSource failed, error:%s", err.Error()) return &datamesh.QueryDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrQueryDomainDataSource, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrQueryDomainDataSource, err.Error()), } } @@ -145,20 +145,20 @@ func (s domainDataSourceService) QueryDomainDataSource(ctx context.Context, requ info, err = s.getDsInfoByKey(ctx, kusciaDomainDataSource.Spec.Type, kusciaDomainDataSource.Spec.InfoKey) if err != nil { return &datamesh.QueryDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrQueryDomainDataSource, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrQueryDomainDataSource, err.Error()), } } } else { encryptedInfo, exist := kusciaDomainDataSource.Spec.Data[encryptedInfo] if !exist { return &datamesh.QueryDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrQueryDomainDataSource, "datasource crd encryptedInfo field is not exist"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrQueryDomainDataSource, "datasource crd encryptedInfo field is not exist"), } } info, err = s.decryptInfo(encryptedInfo) if err != nil { return &datamesh.QueryDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrQueryDomainDataSource, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_DataMeshErrQueryDomainDataSource, err.Error()), } } } @@ -178,12 +178,12 @@ func (s domainDataSourceService) QueryDomainDataSource(ctx context.Context, requ return resp } -//nolint:dupl +// nolint:dulp func (s domainDataSourceService) getDsInfoByKey(ctx context.Context, sourceType string, infoKey string) (*datamesh.DataSourceInfo, error) { response := s.configurationService.QueryConfiguration(ctx, &confmanager.QueryConfigurationRequest{ Ids: []string{infoKey}, }, s.conf.KubeNamespace) - if response.Status.Code != utils.ResponseCodeSuccess { + if !utils.IsSuccessCode(response.Status.Code) { nlog.Errorf("Query info key failed, code: %d, message: %s", response.Status.Code, response.Status.Message) return nil, fmt.Errorf("query info key failed") } @@ -203,7 +203,7 @@ func (s domainDataSourceService) getDsInfoByKey(ctx context.Context, sourceType return info, err } -//nolint:dupl +// nolint:dulp func parseDataSourceURI(sourceType string, info *datamesh.DataSourceInfo) (uri string, err error) { if info == nil { return "", errors.New("info is nil") @@ -241,7 +241,7 @@ func parseDataSourceURI(sourceType string, info *datamesh.DataSourceInfo) (uri s return } -//nolint:dupl +// nolint:dulp func (s domainDataSourceService) encryptInfo(dataSourceType string, info *datamesh.DataSourceInfo) (uri string, encInfo string, err error) { uri, err = parseDataSourceURI(dataSourceType, info) if err != nil { @@ -263,7 +263,7 @@ func (s domainDataSourceService) encryptInfo(dataSourceType string, info *datame return } -//nolint:dupl +// nolint:dulp func (s domainDataSourceService) decryptInfo(cipherInfo string) (*datamesh.DataSourceInfo, error) { plaintext, err := tls.DecryptOAEP(s.conf.DomainKey, cipherInfo) if err != nil { diff --git a/pkg/common/merge_crd.go b/pkg/datamesh/service/merge_crd.go similarity index 99% rename from pkg/common/merge_crd.go rename to pkg/datamesh/service/merge_crd.go index 9fa3a47d..3588aeb2 100644 --- a/pkg/common/merge_crd.go +++ b/pkg/datamesh/service/merge_crd.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package common +package service import ( "k8s.io/apimachinery/pkg/util/json" diff --git a/pkg/datamesh/v1handler/README.md b/pkg/datamesh/v1handler/README.md new file mode 100644 index 00000000..e69de29b diff --git a/pkg/datamesh/handler/grpchandler/simple_handler.go b/pkg/datamesh/v1handler/grpchandler/raw_datamgr_handler.go similarity index 100% rename from pkg/datamesh/handler/grpchandler/simple_handler.go rename to pkg/datamesh/v1handler/grpchandler/raw_datamgr_handler.go diff --git a/pkg/datamesh/handler/httphandler/domaindata/create.go b/pkg/datamesh/v1handler/httphandler/domaindata/create.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindata/create.go rename to pkg/datamesh/v1handler/httphandler/domaindata/create.go index 9940b434..f5212e5b 100644 --- a/pkg/datamesh/handler/httphandler/domaindata/create.go +++ b/pkg/datamesh/v1handler/httphandler/domaindata/create.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/datamesh/handler/httphandler/domaindata/delete.go b/pkg/datamesh/v1handler/httphandler/domaindata/delete.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindata/delete.go rename to pkg/datamesh/v1handler/httphandler/domaindata/delete.go index 15a431dd..ae0a6a43 100644 --- a/pkg/datamesh/handler/httphandler/domaindata/delete.go +++ b/pkg/datamesh/v1handler/httphandler/domaindata/delete.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/datamesh/handler/httphandler/domaindata/query.go b/pkg/datamesh/v1handler/httphandler/domaindata/query.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindata/query.go rename to pkg/datamesh/v1handler/httphandler/domaindata/query.go index 4e7847cb..a701015d 100644 --- a/pkg/datamesh/handler/httphandler/domaindata/query.go +++ b/pkg/datamesh/v1handler/httphandler/domaindata/query.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/datamesh/handler/httphandler/domaindata/update.go b/pkg/datamesh/v1handler/httphandler/domaindata/update.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindata/update.go rename to pkg/datamesh/v1handler/httphandler/domaindata/update.go index 9f687c00..f9d9c222 100644 --- a/pkg/datamesh/handler/httphandler/domaindata/update.go +++ b/pkg/datamesh/v1handler/httphandler/domaindata/update.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/datamesh/handler/httphandler/domaindatagrant/create.go b/pkg/datamesh/v1handler/httphandler/domaindatagrant/create.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindatagrant/create.go rename to pkg/datamesh/v1handler/httphandler/domaindatagrant/create.go index c71f1243..bbbded58 100644 --- a/pkg/datamesh/handler/httphandler/domaindatagrant/create.go +++ b/pkg/datamesh/v1handler/httphandler/domaindatagrant/create.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindatagrant import ( diff --git a/pkg/datamesh/handler/httphandler/domaindatagrant/delete.go b/pkg/datamesh/v1handler/httphandler/domaindatagrant/delete.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindatagrant/delete.go rename to pkg/datamesh/v1handler/httphandler/domaindatagrant/delete.go index e43f2782..ceeee239 100644 --- a/pkg/datamesh/handler/httphandler/domaindatagrant/delete.go +++ b/pkg/datamesh/v1handler/httphandler/domaindatagrant/delete.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindatagrant import ( diff --git a/pkg/datamesh/handler/httphandler/domaindatagrant/query.go b/pkg/datamesh/v1handler/httphandler/domaindatagrant/query.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindatagrant/query.go rename to pkg/datamesh/v1handler/httphandler/domaindatagrant/query.go index a42041d4..da4888c9 100644 --- a/pkg/datamesh/handler/httphandler/domaindatagrant/query.go +++ b/pkg/datamesh/v1handler/httphandler/domaindatagrant/query.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindatagrant import ( diff --git a/pkg/datamesh/handler/httphandler/domaindatagrant/update.go b/pkg/datamesh/v1handler/httphandler/domaindatagrant/update.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindatagrant/update.go rename to pkg/datamesh/v1handler/httphandler/domaindatagrant/update.go index 84e449da..d51d019b 100644 --- a/pkg/datamesh/handler/httphandler/domaindatagrant/update.go +++ b/pkg/datamesh/v1handler/httphandler/domaindatagrant/update.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindatagrant import ( diff --git a/pkg/datamesh/handler/httphandler/domaindatasource/query.go b/pkg/datamesh/v1handler/httphandler/domaindatasource/query.go similarity index 99% rename from pkg/datamesh/handler/httphandler/domaindatasource/query.go rename to pkg/datamesh/v1handler/httphandler/domaindatasource/query.go index 2748d6a2..1edd0907 100644 --- a/pkg/datamesh/handler/httphandler/domaindatasource/query.go +++ b/pkg/datamesh/v1handler/httphandler/domaindatasource/query.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindatasource import ( diff --git a/scripts/deploy/cgroup_pre_detect.sh b/pkg/embedstrings/cgroup_pre_detect.sh similarity index 100% rename from scripts/deploy/cgroup_pre_detect.sh rename to pkg/embedstrings/cgroup_pre_detect.sh diff --git a/scripts/deploy/iptables_pre_detect.sh b/pkg/embedstrings/iptables_pre_detect.sh similarity index 100% rename from scripts/deploy/iptables_pre_detect.sh rename to pkg/embedstrings/iptables_pre_detect.sh diff --git a/pkg/embedstrings/strings.go b/pkg/embedstrings/strings.go new file mode 100644 index 00000000..1fb06b28 --- /dev/null +++ b/pkg/embedstrings/strings.go @@ -0,0 +1,24 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//nolint:all +package embedstrings + +import _ "embed" + +//go:embed cgroup_pre_detect.sh +var CGrouptPreDetectScript string + +//go:embed iptables_pre_detect.sh +var IPTablesPreDetectScript string diff --git a/pkg/gateway/controller/domain_route.go b/pkg/gateway/controller/domain_route.go index e4137b90..f5cfc1c6 100644 --- a/pkg/gateway/controller/domain_route.go +++ b/pkg/gateway/controller/domain_route.go @@ -203,7 +203,7 @@ func (c *DomainRouteController) Run(ctx context.Context, threadiness int, stopCh } func (c *DomainRouteController) checkConnectionHealthy(ctx context.Context, stopCh <-chan struct{}) { - t := time.NewTicker(15 * time.Second) + t := time.NewTicker(common.GatewayHealthCheckDuration) defer t.Stop() for { select { diff --git a/pkg/gateway/controller/handshake.go b/pkg/gateway/controller/handshake.go index a88db984..5b012531 100644 --- a/pkg/gateway/controller/handshake.go +++ b/pkg/gateway/controller/handshake.go @@ -432,7 +432,16 @@ func (c *DomainRouteController) checkTokenStatus(domainID, tokenRevision string) } drName := common.GenDomainRouteName(domainID, c.gateway.Namespace) if dr, err := c.domainRouteLister.DomainRoutes(c.gateway.Namespace).Get(drName); err == nil { - return checkTokenRevision(tokenRevision, dr) + destStatus := checkTokenRevision(tokenRevision, dr) + if destStatus == TokenReady { + index, token := IndexToken(tokenRevision, dr) + token.HeartBeatTime = metav1.Now() + dr.Status.TokenStatus.Tokens[index] = token + if _, err = c.kusciaClient.KusciaV1alpha1().DomainRoutes(dr.Namespace).UpdateStatus(context.Background(), dr, metav1.UpdateOptions{}); err != nil { + nlog.Errorf("update domain route fail, detail -> %v", err) + } + } + return destStatus } else if k8serrors.IsNotFound(err) { return NoAuthentication } @@ -443,19 +452,24 @@ func checkTokenRevision(tokenRevision string, dr *kusciaapisv1alpha1.DomainRoute if tokenRevision == "" { return TokenRevisionInputInvalid } - r, err := strconv.Atoi(tokenRevision) - if err != nil { - return TokenRevisionInputInvalid + + index, token := IndexToken(tokenRevision, dr) + if index == -1 { + return TokenNotFound } - for _, t := range dr.Status.TokenStatus.Tokens { - if t.Revision == int64(r) { - if t.IsReady { - return TokenReady - } - return TokenNotReady + if token.IsReady { + return TokenReady + } + return TokenNotReady +} + +func IndexToken(revision string, dr *kusciaapisv1alpha1.DomainRoute) (int, kusciaapisv1alpha1.DomainRouteToken) { + for index, t := range dr.Status.TokenStatus.Tokens { + if strconv.FormatInt(t.Revision, 10) == revision { + return index, t } } - return TokenNotFound + return -1, kusciaapisv1alpha1.DomainRouteToken{} } func buildFailedHandshakeReply(code int32, err error) *handshake.HandShakeResponse { diff --git a/pkg/gateway/controller/register_node_test.go b/pkg/gateway/controller/register_node_test.go index abf554cc..c9f9e5ce 100644 --- a/pkg/gateway/controller/register_node_test.go +++ b/pkg/gateway/controller/register_node_test.go @@ -18,6 +18,7 @@ import ( "crypto/rsa" "encoding/base64" "fmt" + "net/http" "reflect" "testing" "time" @@ -25,7 +26,10 @@ import ( "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" + gomonkeyv2 "github.com/agiledragon/gomonkey/v2" + "github.com/secretflow/kuscia/cmd/kuscia/confloader" + "github.com/secretflow/kuscia/pkg/gateway/utils" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/tls" "github.com/secretflow/kuscia/proto/api/v1alpha1/handshake" @@ -100,7 +104,7 @@ func generateTestKey(t *testing.T, namespace string) (csr string, key *rsa.Priva if err != nil { t.Errorf("Generate key data failed, error: %s.", err.Error()) } - rawKey, err := base64.StdEncoding.DecodeString(keyStr) + rawKey, _ := base64.StdEncoding.DecodeString(keyStr) key, err = tls.ParseKey(rawKey, "") if err != nil { t.Errorf("Parse key data failed, error: %s.", err.Error()) @@ -109,12 +113,22 @@ func generateTestKey(t *testing.T, namespace string) (csr string, key *rsa.Priva return } -func TestRegisterDomain(t *testing.T) { +func TestRegisterDomain_ServerNotExists(t *testing.T) { + t.Parallel() csr, key := generateTestKey(t, utAlice) - _ = RegisterDomain("alice", "test", csr, key, nil) + + // try to mock http request + gomonkeyv2.ApplyFunc(utils.DoHTTPWithRetry, func(i interface{}, out interface{}, hp *utils.HTTPParam, d time.Duration, tm int) error { + assert.Equal(t, http.MethodPost, hp.Method) + assert.Equal(t, utAlice, hp.KusciaSource) + return nil + }) + + assert.NoError(t, RegisterDomain("alice", "test", csr, key, nil)) } func TestVerifyRequest(t *testing.T) { + t.Parallel() csr, key := generateTestKey(t, utAlice) req, token, err := generateJwtToken(utAlice, csr, key) assert.NoError(t, err, "generateJwtToken failed") @@ -123,6 +137,7 @@ func TestVerifyRequest(t *testing.T) { } func TestVerifyCSRcn(t *testing.T) { + t.Parallel() // request domain is alice but csr is bob csr, key := generateTestKey(t, utBob) req, token, err := generateJwtToken(utAlice, csr, key) @@ -132,6 +147,7 @@ func TestVerifyCSRcn(t *testing.T) { } func TestCompatibility(t *testing.T) { + t.Parallel() // token md5 vs claim (ma5\sha256) csr, key := generateTestKey(t, utAlice) req, token, err := generateJwtTokenMd5(utAlice, csr, key) diff --git a/pkg/gateway/utils/transit_test.go b/pkg/gateway/utils/transit_test.go index 041c6623..59e51036 100644 --- a/pkg/gateway/utils/transit_test.go +++ b/pkg/gateway/utils/transit_test.go @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl package utils import ( diff --git a/pkg/gateway/xds/xds.go b/pkg/gateway/xds/xds.go index b67ce1cf..971daba8 100644 --- a/pkg/gateway/xds/xds.go +++ b/pkg/gateway/xds/xds.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package xds import ( diff --git a/pkg/interconn/kuscia/controller.go b/pkg/interconn/kuscia/controller.go index f905d1a8..abe3e857 100644 --- a/pkg/interconn/kuscia/controller.go +++ b/pkg/interconn/kuscia/controller.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( @@ -295,8 +295,7 @@ func filterTask(obj metav1.Object) bool { func matchAnnotations(annotations map[string]string) bool { if annotations == nil || annotations[common.InitiatorAnnotationKey] == "" || - annotations[common.InterConnKusciaPartyAnnotationKey] == "" || - annotations[common.InterConnSelfPartyAnnotationKey] == "" { + annotations[common.InterConnKusciaPartyAnnotationKey] == "" { return false } @@ -355,7 +354,6 @@ func (c *Controller) Run(workers int) error { }() c.hostResourceManager.SetWorkers(workers) - nlog.Infof("Starting %v", c.Name()) c.kusciaInformerFactory.Start(c.ctx.Done()) @@ -370,6 +368,14 @@ func (c *Controller) Run(workers int) error { return fmt.Errorf("failed to wait for cache sync for %v", c.Name()) } c.registerInteropConfigs() + for { + if c.hostResourceManager.HasSynced() { + nlog.Info("Host resource manager finish syncing resources") + break + } + nlog.Info("Waiting for host resource manager to sync resources...") + time.Sleep(2 * time.Second) + } nlog.Infof("Starting %v workers to handle object for %v", workers, c.Name()) for i := 0; i < workers; i++ { diff --git a/pkg/interconn/kuscia/controller_test.go b/pkg/interconn/kuscia/controller_test.go index 806399da..9d01a202 100644 --- a/pkg/interconn/kuscia/controller_test.go +++ b/pkg/interconn/kuscia/controller_test.go @@ -25,6 +25,7 @@ import ( ) func TestResourceFilter(t *testing.T) { + t.Parallel() tests := []struct { name string obj interface{} @@ -117,6 +118,7 @@ func TestResourceFilter(t *testing.T) { } func TestFilterJob(t *testing.T) { + t.Parallel() tests := []struct { name string obj metav1.Object @@ -157,6 +159,7 @@ func TestFilterJob(t *testing.T) { } func TestFilterDeployment(t *testing.T) { + t.Parallel() tests := []struct { name string obj metav1.Object @@ -197,6 +200,7 @@ func TestFilterDeployment(t *testing.T) { } func TestFilterTask(t *testing.T) { + t.Parallel() tests := []struct { name string obj metav1.Object @@ -254,6 +258,7 @@ func TestFilterTask(t *testing.T) { } func TestFilterTaskResource(t *testing.T) { + t.Parallel() tests := []struct { name string obj metav1.Object @@ -322,6 +327,7 @@ func TestFilterTaskResource(t *testing.T) { } func TestFilterResourceSummary(t *testing.T) { + t.Parallel() now := metav1.Now() tests := []struct { name string diff --git a/pkg/interconn/kuscia/deployment.go b/pkg/interconn/kuscia/deployment.go index 972db05a..a1c23b87 100644 --- a/pkg/interconn/kuscia/deployment.go +++ b/pkg/interconn/kuscia/deployment.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( diff --git a/pkg/interconn/kuscia/deployment_test.go b/pkg/interconn/kuscia/deployment_test.go index 17accdf4..06f4fc6e 100644 --- a/pkg/interconn/kuscia/deployment_test.go +++ b/pkg/interconn/kuscia/deployment_test.go @@ -44,6 +44,7 @@ func makeMockDeployment(namespace, name string) *v1alpha1.KusciaDeployment { } func TestHandleUpdatedDeployment(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -91,6 +92,7 @@ func TestHandleUpdatedDeployment(t *testing.T) { } func TestHandleDeletedDeployment(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) domainInformer := kusciaInformerFactory.Kuscia().V1alpha1().Domains() @@ -126,6 +128,7 @@ func TestHandleDeletedDeployment(t *testing.T) { } func TestDeleteDeploymentCascadedResources(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) kdInformer := kusciaInformerFactory.Kuscia().V1alpha1().KusciaDeployments() @@ -147,6 +150,7 @@ func TestDeleteDeploymentCascadedResources(t *testing.T) { } func TestCreateOrUpdateMirrorDeployments(t *testing.T) { + t.Parallel() ctx := context.Background() bobKd := makeMockDeployment("bob", "kd-1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(bobKd) @@ -185,6 +189,7 @@ func TestCreateOrUpdateMirrorDeployments(t *testing.T) { } func TestCreateOrUpdateDeploymentSummary(t *testing.T) { + t.Parallel() ctx := context.Background() bobKds := makeMockDeploymentSummary("bob", "kd-1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(bobKds) @@ -233,6 +238,7 @@ func TestCreateOrUpdateDeploymentSummary(t *testing.T) { } func TestProcessDeploymentAsPartner(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -296,6 +302,7 @@ func TestProcessDeploymentAsPartner(t *testing.T) { } func TestUpdateDeploymentSummaryPartyStatus(t *testing.T) { + t.Parallel() // kd status is failed and is not equal to kds, should return true kd := makeMockDeployment("cross-domain", "kd-1") kd.Status.Phase = v1alpha1.KusciaDeploymentPhaseFailed diff --git a/pkg/interconn/kuscia/deploysummary.go b/pkg/interconn/kuscia/deploysummary.go index 126bb07e..e12cbe05 100644 --- a/pkg/interconn/kuscia/deploysummary.go +++ b/pkg/interconn/kuscia/deploysummary.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( diff --git a/pkg/interconn/kuscia/deploysummary_test.go b/pkg/interconn/kuscia/deploysummary_test.go index 75faf8fa..d8eb1832 100644 --- a/pkg/interconn/kuscia/deploysummary_test.go +++ b/pkg/interconn/kuscia/deploysummary_test.go @@ -41,6 +41,7 @@ func makeMockDeploymentSummary(namespace, name string) *v1alpha1.KusciaDeploymen } func TestHandleUpdatedDeploymentSummary(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -88,6 +89,7 @@ func TestHandleUpdatedDeploymentSummary(t *testing.T) { } func TestUpdateDeployment(t *testing.T) { + t.Parallel() ctx := context.Background() kd := makeMockDeployment("cross-domain", "kd-1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kd) @@ -126,6 +128,7 @@ func TestUpdateDeployment(t *testing.T) { } func TestUpdateDeploymentStatus(t *testing.T) { + t.Parallel() // kds status is failed and is not equal to kd, should return true kds := makeMockDeploymentSummary("alice", "kd-1") kds.Status.Phase = v1alpha1.KusciaDeploymentPhaseFailed diff --git a/pkg/interconn/kuscia/hostresources/deployment.go b/pkg/interconn/kuscia/hostresources/deployment.go index dac51929..563d8cb7 100644 --- a/pkg/interconn/kuscia/hostresources/deployment.go +++ b/pkg/interconn/kuscia/hostresources/deployment.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package hostresources import ( diff --git a/pkg/interconn/kuscia/hostresources/deployment_test.go b/pkg/interconn/kuscia/hostresources/deployment_test.go index 0168726a..a1f5dbf0 100644 --- a/pkg/interconn/kuscia/hostresources/deployment_test.go +++ b/pkg/interconn/kuscia/hostresources/deployment_test.go @@ -41,6 +41,7 @@ func makeMockDeployment(namespace, name string) *v1alpha1.KusciaDeployment { } func TestHandleUpdatedDeployment(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -91,6 +92,7 @@ func TestHandleUpdatedDeployment(t *testing.T) { } func TestHandleDeletedDeployment(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -108,6 +110,7 @@ func TestHandleDeletedDeployment(t *testing.T) { } func TestDeleteDeployment(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := fake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -140,6 +143,7 @@ func TestDeleteDeployment(t *testing.T) { } func TestProcessDeployment(t *testing.T) { + t.Parallel() ctx := context.Background() kd1 := makeMockDeployment("cross-domain", "kd-1") kusciaFakeClient := fake.NewSimpleClientset(kd1) diff --git a/pkg/interconn/kuscia/hostresources/deploymentsummary.go b/pkg/interconn/kuscia/hostresources/deploymentsummary.go index 0901a26f..b006dfeb 100644 --- a/pkg/interconn/kuscia/hostresources/deploymentsummary.go +++ b/pkg/interconn/kuscia/hostresources/deploymentsummary.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package hostresources import ( diff --git a/pkg/interconn/kuscia/hostresources/deploymentsummary_test.go b/pkg/interconn/kuscia/hostresources/deploymentsummary_test.go index 081438cd..f68a4ef3 100644 --- a/pkg/interconn/kuscia/hostresources/deploymentsummary_test.go +++ b/pkg/interconn/kuscia/hostresources/deploymentsummary_test.go @@ -41,6 +41,7 @@ func makeMockDeploymentSummary(namespace, name string) *v1alpha1.KusciaDeploymen } func TestHandleUpdatedDeploymentSummary(t *testing.T) { + t.Parallel() kusciaFakeClient := fake.NewSimpleClientset() opt := &hostResourcesControllerOptions{ host: "alice", @@ -93,6 +94,7 @@ func TestHandleUpdatedDeploymentSummary(t *testing.T) { } func TestUpdateMemberDeployment(t *testing.T) { + t.Parallel() ctx := context.Background() kd := makeMockDeployment("cross-domain", "kd-1") kusciaFakeClient := fake.NewSimpleClientset(kd) @@ -149,6 +151,7 @@ func TestUpdateMemberDeployment(t *testing.T) { } func TestUpdateDeploymentStatus(t *testing.T) { + t.Parallel() // kd status is empty, should return true kd := makeMockDeployment("cross-domain", "kd-1") kds := makeMockDeploymentSummary("alice", "kd-1") diff --git a/pkg/interconn/kuscia/hostresources/domaindata.go b/pkg/interconn/kuscia/hostresources/domaindata.go index abe6c1b1..93d677f2 100644 --- a/pkg/interconn/kuscia/hostresources/domaindata.go +++ b/pkg/interconn/kuscia/hostresources/domaindata.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package hostresources import ( diff --git a/pkg/interconn/kuscia/hostresources/domaindata_test.go b/pkg/interconn/kuscia/hostresources/domaindata_test.go index 5825774d..d731bdf3 100644 --- a/pkg/interconn/kuscia/hostresources/domaindata_test.go +++ b/pkg/interconn/kuscia/hostresources/domaindata_test.go @@ -40,6 +40,7 @@ func makeMockDomainData(namespace, name string) *kusciaapisv1alpha1.DomainData { } func TestHandleUpdatedDomainData(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -90,6 +91,7 @@ func TestHandleUpdatedDomainData(t *testing.T) { } func TestHandleDeletedDomainData(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -117,6 +119,7 @@ func TestHandleDeletedDomainData(t *testing.T) { } func TestDeleteDomainData(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := fake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -151,6 +154,7 @@ func TestDeleteDomainData(t *testing.T) { } func TestCreateDomainData(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := fake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -179,6 +183,7 @@ func TestCreateDomainData(t *testing.T) { } func TestUpdateDomainData(t *testing.T) { + t.Parallel() ctx := context.Background() dd := makeMockDomainData("bob", "dd") kusciaFakeClient := fake.NewSimpleClientset(dd) diff --git a/pkg/interconn/kuscia/hostresources/domaindatagrant.go b/pkg/interconn/kuscia/hostresources/domaindatagrant.go index ee8c9d54..935dff1d 100644 --- a/pkg/interconn/kuscia/hostresources/domaindatagrant.go +++ b/pkg/interconn/kuscia/hostresources/domaindatagrant.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package hostresources import ( diff --git a/pkg/interconn/kuscia/hostresources/domaindatagrant_test.go b/pkg/interconn/kuscia/hostresources/domaindatagrant_test.go index 15a7b553..e5d5076e 100644 --- a/pkg/interconn/kuscia/hostresources/domaindatagrant_test.go +++ b/pkg/interconn/kuscia/hostresources/domaindatagrant_test.go @@ -40,6 +40,7 @@ func makeMockDomainDataGrant(namespace, name string) *kusciaapisv1alpha1.DomainD } func TestHandleUpdatedDomainDataGrant(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -90,6 +91,7 @@ func TestHandleUpdatedDomainDataGrant(t *testing.T) { } func TestHandleDeletedDomainDataGrant(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -117,6 +119,7 @@ func TestHandleDeletedDomainDataGrant(t *testing.T) { } func TestDeleteDomainDataGrant(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := fake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -151,6 +154,7 @@ func TestDeleteDomainDataGrant(t *testing.T) { } func TestCreateDomainDataGrant(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := fake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -179,6 +183,7 @@ func TestCreateDomainDataGrant(t *testing.T) { } func TestUpdateDomainDataGrant(t *testing.T) { + t.Parallel() ctx := context.Background() ddg := makeMockDomainDataGrant("bob", "kddg") kusciaFakeClient := fake.NewSimpleClientset(ddg) diff --git a/pkg/interconn/kuscia/hostresources/job.go b/pkg/interconn/kuscia/hostresources/job.go index 645c83c8..ad9eb342 100644 --- a/pkg/interconn/kuscia/hostresources/job.go +++ b/pkg/interconn/kuscia/hostresources/job.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package hostresources import ( diff --git a/pkg/interconn/kuscia/hostresources/job_test.go b/pkg/interconn/kuscia/hostresources/job_test.go index be40b687..bd784ade 100644 --- a/pkg/interconn/kuscia/hostresources/job_test.go +++ b/pkg/interconn/kuscia/hostresources/job_test.go @@ -44,6 +44,7 @@ func makeMockJob(namespace, name string) *v1alpha1.KusciaJob { } func TestHandleUpdatedJob(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -94,6 +95,7 @@ func TestHandleUpdatedJob(t *testing.T) { } func TestHandleDeletedJob(t *testing.T) { + t.Parallel() opt := &hostResourcesControllerOptions{ host: "alice", member: "bob", @@ -111,6 +113,7 @@ func TestHandleDeletedJob(t *testing.T) { } func TestSyncJobHandler(t *testing.T) { + t.Parallel() ctx := context.Background() hostKj1 := makeMockJob("bob", "kj-1") hostKj2 := makeMockJob("bob", "kj-2") diff --git a/pkg/interconn/kuscia/hostresources/jobsummary.go b/pkg/interconn/kuscia/hostresources/jobsummary.go index de519962..f8dec6c6 100644 --- a/pkg/interconn/kuscia/hostresources/jobsummary.go +++ b/pkg/interconn/kuscia/hostresources/jobsummary.go @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package hostresources import ( "context" + "fmt" "reflect" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -75,8 +76,18 @@ func (c *hostResourcesController) syncJobSummaryHandler(ctx context.Context, key if err != nil { // JobSummary is deleted under host cluster if k8serrors.IsNotFound(err) { - nlog.Infof("JobSummary %v may be deleted under host %v cluster, skip processing it", key, c.host) - return nil + _, jobErr := c.hostKusciaClient.KusciaV1alpha1().KusciaJobs(namespace).Get(ctx, name, metav1.GetOptions{}) + if jobErr != nil { + if k8serrors.IsNotFound(jobErr) { + nlog.Infof("Host job %s/%s is not found, delete job %v", namespace, name, name) + err = c.memberKusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Delete(ctx, name, metav1.DeleteOptions{}) + if k8serrors.IsNotFound(err) { + return nil + } + return err + } + return fmt.Errorf("failed to get host job %s/%s, %v", namespace, name, jobErr) + } } return err } @@ -87,7 +98,16 @@ func (c *hostResourcesController) syncJobSummaryHandler(ctx context.Context, key func (c *hostResourcesController) updateMemberJobByJobSummary(ctx context.Context, jobSummary *kusciaapisv1alpha1.KusciaJobSummary) error { originalJob, err := c.memberJobLister.KusciaJobs(common.KusciaCrossDomain).Get(jobSummary.Name) if err != nil { - return err + if k8serrors.IsNotFound(err) { + originalJob, err = c.memberKusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Get(ctx, jobSummary.Name, metav1.GetOptions{}) + if err != nil && k8serrors.IsNotFound(err) { + nlog.Infof("Job %s/%s is not found, skip processing it", common.KusciaCrossDomain, jobSummary.Name) + return nil + } + } + if err != nil { + return err + } } selfDomainIDs := ikcommon.GetSelfClusterPartyDomainIDs(originalJob) @@ -123,6 +143,10 @@ func (c *hostResourcesController) updateMemberJobByJobSummary(ctx context.Contex needUpdate = true } + if updateJobStatusPhase(job, jobSummary) { + needUpdate = true + } + if needUpdate { job.Status.LastReconcileTime = ikcommon.GetCurrentTime() if _, err = c.memberKusciaClient.KusciaV1alpha1().KusciaJobs(job.Namespace).UpdateStatus(ctx, job, metav1.UpdateOptions{}); err != nil { @@ -132,6 +156,23 @@ func (c *hostResourcesController) updateMemberJobByJobSummary(ctx context.Contex return nil } +func updateJobStatusPhase(job *kusciaapisv1alpha1.KusciaJob, jobSummary *kusciaapisv1alpha1.KusciaJobSummary) bool { + if jobSummary.Status.Phase == kusciaapisv1alpha1.KusciaJobFailed && + jobSummary.Status.Phase != job.Status.Phase && + jobSummary.Status.CompletionTime != nil && + jobSummary.Status.Reason != "" && + jobSummary.Status.Reason != job.Status.Reason { + switch jobSummary.Status.Reason { + case string(kusciaapisv1alpha1.ValidateFailed), string(kusciaapisv1alpha1.CreateTaskFailed): + job.Status.Phase = jobSummary.Status.Phase + job.Status.Reason = jobSummary.Status.Reason + job.Status.Message = jobSummary.Status.Message + return true + } + } + return false +} + func updateJobApproveStatus(job *kusciaapisv1alpha1.KusciaJob, jobSummary *kusciaapisv1alpha1.KusciaJobSummary, domainIDMap map[string]struct{}) bool { if len(job.Status.ApproveStatus) == 0 && len(jobSummary.Status.ApproveStatus) > 0 { job.Status.ApproveStatus = jobSummary.Status.ApproveStatus diff --git a/pkg/interconn/kuscia/hostresources/jobsummary_test.go b/pkg/interconn/kuscia/hostresources/jobsummary_test.go index 9fc03a64..63f9bcf5 100644 --- a/pkg/interconn/kuscia/hostresources/jobsummary_test.go +++ b/pkg/interconn/kuscia/hostresources/jobsummary_test.go @@ -40,6 +40,7 @@ func makeMockJobSummary(namespace, name string) *v1alpha1.KusciaJobSummary { } func TestHandleUpdatedJobSummary(t *testing.T) { + t.Parallel() kusciaFakeClient := fake.NewSimpleClientset() opt := &hostResourcesControllerOptions{ host: "alice", @@ -92,6 +93,7 @@ func TestHandleUpdatedJobSummary(t *testing.T) { } func TestUpdateMemberJobByJobSummary(t *testing.T) { + t.Parallel() ctx := context.Background() kj := makeMockJob("cross-domain", "kj-1") kj.Annotations[common.InterConnSelfPartyAnnotationKey] = "bob" @@ -112,10 +114,10 @@ func TestUpdateMemberJobByJobSummary(t *testing.T) { t.Error("new controller failed") } - // member job doesn't exist, should return err + // member job doesn't exist, should return nil kjs := makeMockJobSummary("bob", "kj-2") got := c.updateMemberJobByJobSummary(ctx, kjs) - assert.Equal(t, true, got != nil) + assert.Equal(t, nil, got) // self cluster party domain id is empty in job, should return nil kjs = makeMockJobSummary("bob", "kj-1") diff --git a/pkg/interconn/kuscia/hostresources/manager.go b/pkg/interconn/kuscia/hostresources/manager.go index 03f20bb1..8c6b736f 100644 --- a/pkg/interconn/kuscia/hostresources/manager.go +++ b/pkg/interconn/kuscia/hostresources/manager.go @@ -30,6 +30,7 @@ type ResourcesManager interface { SetWorkers(worker int) GetHostResourceAccessor(host, member string) ResourcesAccessor Stop() + HasSynced() bool } // Options defines some options for host resources manager. @@ -150,6 +151,22 @@ func (m *hostResourcesManager) GetHostResourceAccessor(host, member string) Reso return ra } +// HasSynced is used to check if there are already resources that have been synchronized. +func (m *hostResourcesManager) HasSynced() bool { + m.mu.RLock() + defer m.mu.RUnlock() + if len(m.resourceControllers) == 0 { + return true + } + + for _, rc := range m.resourceControllers { + if rc.HasSynced() { + return true + } + } + return false +} + // generateResourceControllerKey is used to generate resource controller key. func generateResourceControllerKey(host, member string) string { return host + "/" + member diff --git a/pkg/interconn/kuscia/hostresources/tasksummary.go b/pkg/interconn/kuscia/hostresources/tasksummary.go index 7fe09cda..c83870a3 100644 --- a/pkg/interconn/kuscia/hostresources/tasksummary.go +++ b/pkg/interconn/kuscia/hostresources/tasksummary.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package hostresources import ( diff --git a/pkg/interconn/kuscia/hostresources/tasksummary_test.go b/pkg/interconn/kuscia/hostresources/tasksummary_test.go index 531a47d1..b5d0ea66 100644 --- a/pkg/interconn/kuscia/hostresources/tasksummary_test.go +++ b/pkg/interconn/kuscia/hostresources/tasksummary_test.go @@ -70,6 +70,7 @@ func makeMockTaskResource(namespace, name string) *v1alpha1.TaskResource { } func TestHandleUpdatedTaskSummary(t *testing.T) { + t.Parallel() kusciaFakeClient := fake.NewSimpleClientset() opt := &hostResourcesControllerOptions{ host: "alice", @@ -120,8 +121,8 @@ func TestHandleUpdatedTaskSummary(t *testing.T) { } func TestSyncTaskSummaryHandler(t *testing.T) { + t.Parallel() ctx := context.Background() - kts1 := makeMockTaskSummary("bob", "task-1", "1") kts1.Spec.Alias = "" kts2 := makeMockTaskSummary("bob", "task-2", "2") @@ -185,8 +186,8 @@ func TestSyncTaskSummaryHandler(t *testing.T) { } func TestUpdateMemberJobByTaskSummary(t *testing.T) { + t.Parallel() ctx := context.Background() - hostKusciaFakeClient := fake.NewSimpleClientset() GetHostClient = func(token, masterURL string) (*kubeconfig.KubeClients, error) { return &kubeconfig.KubeClients{ @@ -234,6 +235,7 @@ func TestUpdateMemberJobByTaskSummary(t *testing.T) { } func TestUpdateMemberTask(t *testing.T) { + t.Parallel() // status is Running in task summary, should return false kt := makeMockTask("cross-domain", "task-1") kt.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{ @@ -306,8 +308,8 @@ func TestUpdateMemberTask(t *testing.T) { } func TestUpdateMemberTaskResource(t *testing.T) { + t.Parallel() ctx := context.Background() - hostKusciaFakeClient := fake.NewSimpleClientset() GetHostClient = func(token, masterURL string) (*kubeconfig.KubeClients, error) { return &kubeconfig.KubeClients{ diff --git a/pkg/interconn/kuscia/interopconfig_test.go b/pkg/interconn/kuscia/interopconfig_test.go index a5a9a2d1..439e14ba 100644 --- a/pkg/interconn/kuscia/interopconfig_test.go +++ b/pkg/interconn/kuscia/interopconfig_test.go @@ -29,6 +29,7 @@ import ( ) func TestHandleAddedorDeletedInteropConfig(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -44,6 +45,7 @@ func TestHandleAddedorDeletedInteropConfig(t *testing.T) { } func TestHandleUpdatedInteropConfig(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -91,6 +93,7 @@ func TestHandleUpdatedInteropConfig(t *testing.T) { } func TestRegisterInteropConfig(t *testing.T) { + t.Parallel() hostKubeFakeClient := clientsetfake.NewSimpleClientset() hostKusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() hostresources.GetHostClient = func(token, masterURL string) (*kubeconfig.KubeClients, error) { @@ -168,6 +171,7 @@ func TestRegisterInteropConfig(t *testing.T) { } func TestDeregisterInteropConfig(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -219,6 +223,7 @@ func TestDeregisterInteropConfig(t *testing.T) { } func TestGetInteropConfigInfo(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -266,6 +271,7 @@ func TestGetInteropConfigInfo(t *testing.T) { } func TestSetInteropConfigInfo(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -300,6 +306,7 @@ func TestSetInteropConfigInfo(t *testing.T) { } func TestDeleteInteropConfigInfo(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { diff --git a/pkg/interconn/kuscia/job.go b/pkg/interconn/kuscia/job.go index d9f2f011..607e9d5b 100644 --- a/pkg/interconn/kuscia/job.go +++ b/pkg/interconn/kuscia/job.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( @@ -308,31 +308,48 @@ func (c *Controller) createJobSummary(ctx context.Context, job *v1alpha1.KusciaJ } func (c *Controller) updateJobSummary(ctx context.Context, job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary) error { - domainIDs := ikcommon.GetSelfClusterPartyDomainIDs(job) - if domainIDs == nil { - nlog.Errorf("Failed to get self cluster party domain ids from job %v, skip processing it", ikcommon.GetObjectNamespaceName(job)) - return nil - } - needUpdate := false if job.Status.Phase != jobSummary.Status.Phase { needUpdate = true jobSummary.Status.Phase = job.Status.Phase + if isCompleted(jobSummary) { + jobSummary.Status.CompletionTime = job.Status.CompletionTime + } + } + + if jobSummary.Status.Reason != job.Status.Reason { + needUpdate = true + jobSummary.Status.Reason = job.Status.Reason + } + + if job.Status.Message != jobSummary.Status.Message { + needUpdate = true + if job.Status.Phase == v1alpha1.KusciaJobFailed { + jobParties := job.Annotations[common.InterConnSelfPartyAnnotationKey] + jobSummary.Status.Message = fmt.Sprintf("party[%s] job failed info: %s", jobParties, job.Status.Message) + } else { + jobSummary.Status.Message = job.Status.Message + } } if updateJobSummaryStage(job, jobSummary) { needUpdate = true } - if updateJobSummaryApproveStatus(job, jobSummary, domainIDs) { + domainIDs := ikcommon.GetSelfClusterPartyDomainIDs(job) + if len(domainIDs) == 0 { + nlog.Warnf("The self cluster party domain ids from job %v are empty", ikcommon.GetObjectNamespaceName(job)) + } + + if updateJobSummaryApproveStatus(job, jobSummary, domainIDs, true) { needUpdate = true } - if updateJobSummaryStageStatus(job, jobSummary, domainIDs) { + if updateJobSummaryStageStatus(job, jobSummary, domainIDs, true) { needUpdate = true } - if updateJobSummaryPartyTaskCreateStatus(job, jobSummary, domainIDs) { + if updateJobSummaryPartyTaskCreateStatus(job, jobSummary, domainIDs, true) { needUpdate = true } @@ -356,6 +373,15 @@ func (c *Controller) updateJobSummary(ctx context.Context, job *v1alpha1.KusciaJ return nil } +func isCompleted(jobSummary *v1alpha1.KusciaJobSummary) bool { + if jobSummary.Status.Phase == v1alpha1.KusciaJobSucceeded || + jobSummary.Status.Phase == v1alpha1.KusciaJobFailed || + jobSummary.Status.Phase == v1alpha1.KusciaJobCancelled { + return true + } + return false +} + func updateJobSummaryStage(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary) bool { // if the stage of job changed to cancel, we will prevent updating the job stage if jobSummary.Spec.Stage == v1alpha1.JobCancelStage { @@ -382,7 +408,7 @@ func updateJobSummaryStage(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJ return false } -func updateJobSummaryApproveStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string) bool { +func updateJobSummaryApproveStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string, isHost bool) bool { if len(job.Status.ApproveStatus) == 0 || reflect.DeepEqual(job.Status.ApproveStatus, jobSummary.Status.ApproveStatus) { return false } @@ -393,17 +419,29 @@ func updateJobSummaryApproveStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1 } updated := false - for _, domainID := range domainIDs { - if job.Status.ApproveStatus[domainID] != jobSummary.Status.ApproveStatus[domainID] { - updated = true - jobSummary.Status.ApproveStatus[domainID] = job.Status.ApproveStatus[domainID] + if isHost { + for domainID, status := range job.Status.ApproveStatus { + // party jobSummary under host cluster should be updated by remote member party + if domainID == jobSummary.Namespace { + continue + } + if jobSummary.Status.ApproveStatus[domainID] != status { + updated = true + jobSummary.Status.ApproveStatus[domainID] = status + } + } + } else { + for _, domainID := range domainIDs { + if job.Status.ApproveStatus[domainID] != jobSummary.Status.ApproveStatus[domainID] { + updated = true + jobSummary.Status.ApproveStatus[domainID] = job.Status.ApproveStatus[domainID] + } } } - return updated } -func updateJobSummaryStageStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string) bool { +func updateJobSummaryStageStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string, isHost bool) bool { if len(job.Status.StageStatus) == 0 || reflect.DeepEqual(job.Status.StageStatus, jobSummary.Status.StageStatus) { return false } @@ -414,17 +452,29 @@ func updateJobSummaryStageStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.K } updated := false - for _, domainID := range domainIDs { - if job.Status.StageStatus[domainID] != jobSummary.Status.StageStatus[domainID] { - updated = true - jobSummary.Status.StageStatus[domainID] = job.Status.StageStatus[domainID] + if isHost { + for domainID, status := range job.Status.StageStatus { + if domainID == jobSummary.Namespace { + continue + } + if jobSummary.Status.StageStatus[domainID] != status { + updated = true + jobSummary.Status.StageStatus[domainID] = status + } + } + } else { + for _, domainID := range domainIDs { + if job.Status.StageStatus[domainID] != jobSummary.Status.StageStatus[domainID] { + updated = true + jobSummary.Status.StageStatus[domainID] = job.Status.StageStatus[domainID] + } } } return updated } -func updateJobSummaryPartyTaskCreateStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string) bool { +func updateJobSummaryPartyTaskCreateStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string, isHost bool) bool { if len(job.Status.PartyTaskCreateStatus) == 0 || reflect.DeepEqual(job.Status.PartyTaskCreateStatus, jobSummary.Status.PartyTaskCreateStatus) { return false } @@ -435,10 +485,22 @@ func updateJobSummaryPartyTaskCreateStatus(job *v1alpha1.KusciaJob, jobSummary * } updated := false - for _, domainID := range domainIDs { - if !reflect.DeepEqual(job.Status.PartyTaskCreateStatus[domainID], jobSummary.Status.PartyTaskCreateStatus[domainID]) { - updated = true - jobSummary.Status.PartyTaskCreateStatus[domainID] = job.Status.PartyTaskCreateStatus[domainID] + if isHost { + for domainID, status := range job.Status.PartyTaskCreateStatus { + if domainID == jobSummary.Namespace { + continue + } + if !reflect.DeepEqual(jobSummary.Status.PartyTaskCreateStatus[domainID], status) { + updated = true + jobSummary.Status.PartyTaskCreateStatus[domainID] = job.Status.PartyTaskCreateStatus[domainID] + } + } + } else { + for _, domainID := range domainIDs { + if !reflect.DeepEqual(job.Status.PartyTaskCreateStatus[domainID], jobSummary.Status.PartyTaskCreateStatus[domainID]) { + updated = true + jobSummary.Status.PartyTaskCreateStatus[domainID] = job.Status.PartyTaskCreateStatus[domainID] + } } } @@ -492,7 +554,19 @@ func (c *Controller) processJobAsPartner(ctx context.Context, job *v1alpha1.Kusc if err != nil { nlog.Errorf("Get JobSummary %v from host %v cluster failed, %v", job.Name, initiator, err) if k8serrors.IsNotFound(err) { - return nil + // check mirror job if exist + _, jobErr := hra.HostKusciaClient().KusciaV1alpha1().KusciaJobs(masterDomainID).Get(ctx, job.Name, metav1.GetOptions{}) + if jobErr != nil { + if k8serrors.IsNotFound(jobErr) { + nlog.Infof("Host job %s/%s is not found, delete job %v", masterDomainID, job.Name, ikcommon.GetObjectNamespaceName(job)) + err = c.kusciaClient.KusciaV1alpha1().KusciaJobs(job.Namespace).Delete(ctx, job.Name, metav1.DeleteOptions{}) + if k8serrors.IsNotFound(err) { + return nil + } + return err + } + return fmt.Errorf("failed to get host job %s/%s, %v", masterDomainID, job.Name, jobErr) + } } return err } @@ -530,6 +604,10 @@ func (c *Controller) updateHostJobSummary(ctx context.Context, needUpdate = true } + if updateHostJobSummaryStatusPhase(job, jobSummary) { + needUpdate = true + } + if needUpdate { jobSummary.Status.LastReconcileTime = ikcommon.GetCurrentTime() if _, err := kusciaClient.KusciaV1alpha1().KusciaJobSummaries(jobSummary.Namespace).Update(ctx, jobSummary, metav1.UpdateOptions{}); err != nil { @@ -540,6 +618,25 @@ func (c *Controller) updateHostJobSummary(ctx context.Context, return nil } +func updateHostJobSummaryStatusPhase(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary) bool { + if job.Status.Phase == v1alpha1.KusciaJobFailed && + job.Status.Phase != jobSummary.Status.Phase && + job.Status.CompletionTime != nil && + job.Status.Reason != "" && + job.Status.Reason != jobSummary.Status.Reason { + switch job.Status.Reason { + case string(v1alpha1.ValidateFailed), string(v1alpha1.CreateTaskFailed): + jobParties := job.Annotations[common.InterConnSelfPartyAnnotationKey] + jobSummary.Status.Phase = job.Status.Phase + jobSummary.Status.Reason = job.Status.Reason + jobSummary.Status.Message = fmt.Sprintf("party[%s] job failed info: %s", jobParties, job.Status.Message) + jobSummary.Status.CompletionTime = job.Status.CompletionTime + return true + } + } + return false +} + func updateHostJobSummaryStage(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, masterDomainID string) bool { if jobSummary.Spec.Stage == v1alpha1.JobCancelStage { return false @@ -562,13 +659,13 @@ func updateHostJobSummaryStage(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.Kus } func updateHostJobSummaryApproveStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string) bool { - return updateJobSummaryApproveStatus(job, jobSummary, domainIDs) + return updateJobSummaryApproveStatus(job, jobSummary, domainIDs, false) } func updateHostJobSummaryStageStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string) bool { - return updateJobSummaryStageStatus(job, jobSummary, domainIDs) + return updateJobSummaryStageStatus(job, jobSummary, domainIDs, false) } func updateHostJobSummaryPartyTaskCreateStatus(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string) bool { - return updateJobSummaryPartyTaskCreateStatus(job, jobSummary, domainIDs) + return updateJobSummaryPartyTaskCreateStatus(job, jobSummary, domainIDs, false) } diff --git a/pkg/interconn/kuscia/job_test.go b/pkg/interconn/kuscia/job_test.go index 65e20cae..39cc92ee 100644 --- a/pkg/interconn/kuscia/job_test.go +++ b/pkg/interconn/kuscia/job_test.go @@ -44,6 +44,7 @@ func makeMockJob(namespace, name string) *v1alpha1.KusciaJob { } func TestHandleUpdatedJob(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -91,6 +92,7 @@ func TestHandleUpdatedJob(t *testing.T) { } func TestHandleDeletedJob(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) domainInformer := kusciaInformerFactory.Kuscia().V1alpha1().Domains() @@ -138,6 +140,7 @@ func TestHandleDeletedJob(t *testing.T) { } func TestDeleteJobCascadedResources(t *testing.T) { + t.Parallel() kj := makeMockJob("bob", "job-1") kjs := makeMockJobSummary("bob", "job-1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kj, kjs) @@ -157,6 +160,7 @@ func TestDeleteJobCascadedResources(t *testing.T) { } func TestCreateMirrorJobs(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) jobInformer := kusciaInformerFactory.Kuscia().V1alpha1().KusciaJobs() @@ -178,6 +182,7 @@ func TestCreateMirrorJobs(t *testing.T) { } func TestBuildMirrorJobs(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) jobInformer := kusciaInformerFactory.Kuscia().V1alpha1().KusciaJobs() @@ -224,6 +229,7 @@ func TestBuildMirrorJobs(t *testing.T) { } func TestCreateOrUpdateJobSummary(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -261,6 +267,7 @@ func TestCreateOrUpdateJobSummary(t *testing.T) { } func TestUpdateJobSummaryStage(t *testing.T) { + t.Parallel() // cancel stage: don't update kj := makeMockJob("cross-domain", "job-1") kjs := makeMockJobSummary("bob", "job-1") @@ -284,11 +291,12 @@ func TestUpdateJobSummaryStage(t *testing.T) { } func TestUpdateJobSummaryApproveStatus(t *testing.T) { + t.Parallel() domainIDs := []string{"bob"} // job approve status is empty, should return false kj := makeMockJob("cross-domain", "job-1") kjs := makeMockJobSummary("bob", "job-1") - got := updateJobSummaryApproveStatus(kj, kjs, domainIDs) + got := updateJobSummaryApproveStatus(kj, kjs, domainIDs, true) assert.Equal(t, false, got) // approve status in job and job summary are same, should return false @@ -300,7 +308,7 @@ func TestUpdateJobSummaryApproveStatus(t *testing.T) { kjs.Status.ApproveStatus = map[string]v1alpha1.JobApprovePhase{ "bob": v1alpha1.JobAccepted, } - got = updateJobSummaryApproveStatus(kj, kjs, domainIDs) + got = updateJobSummaryApproveStatus(kj, kjs, domainIDs, true) assert.Equal(t, false, got) // approve status in job summary is empty but not empty in job, should return true @@ -309,7 +317,7 @@ func TestUpdateJobSummaryApproveStatus(t *testing.T) { "bob": v1alpha1.JobAccepted, } kjs = makeMockJobSummary("bob", "job-1") - got = updateJobSummaryApproveStatus(kj, kjs, domainIDs) + got = updateJobSummaryApproveStatus(kj, kjs, domainIDs, true) assert.Equal(t, true, got) // approve status in job and job summary are different, should return true @@ -317,20 +325,45 @@ func TestUpdateJobSummaryApproveStatus(t *testing.T) { kj.Status.ApproveStatus = map[string]v1alpha1.JobApprovePhase{ "bob": v1alpha1.JobAccepted, } + kjs = makeMockJobSummary("joke", "job-1") + kjs.Status.ApproveStatus = map[string]v1alpha1.JobApprovePhase{ + "joke": v1alpha1.JobRejected, + } + got = updateJobSummaryApproveStatus(kj, kjs, domainIDs, true) + assert.Equal(t, true, got) + + // approve status in job and job summary are different for member, should return true + kj = makeMockJob("cross-domain", "job-1") + kj.Status.ApproveStatus = map[string]v1alpha1.JobApprovePhase{ + "bob": v1alpha1.JobAccepted, + } kjs = makeMockJobSummary("bob", "job-1") kjs.Status.ApproveStatus = map[string]v1alpha1.JobApprovePhase{ - "bob": v1alpha1.JobRejected, + "alice": v1alpha1.JobRejected, } - got = updateJobSummaryApproveStatus(kj, kjs, domainIDs) + got = updateJobSummaryApproveStatus(kj, kjs, domainIDs, false) assert.Equal(t, true, got) + + // approve status in job and job summary are same for member, should return false + kj = makeMockJob("cross-domain", "job-1") + kj.Status.ApproveStatus = map[string]v1alpha1.JobApprovePhase{ + "bob": v1alpha1.JobAccepted, + } + kjs = makeMockJobSummary("bob", "job-1") + kjs.Status.ApproveStatus = map[string]v1alpha1.JobApprovePhase{ + "bob": v1alpha1.JobAccepted, + } + got = updateJobSummaryApproveStatus(kj, kjs, domainIDs, false) + assert.Equal(t, false, got) } func TestUpdateJobSummaryStageStatus(t *testing.T) { + t.Parallel() domainIDs := []string{"bob"} // job stage status is empty, should return false kj := makeMockJob("cross-domain", "job-1") kjs := makeMockJobSummary("bob", "job-1") - got := updateJobSummaryStageStatus(kj, kjs, domainIDs) + got := updateJobSummaryStageStatus(kj, kjs, domainIDs, true) assert.Equal(t, false, got) // stage status in job and job summary are same, should return false @@ -342,7 +375,7 @@ func TestUpdateJobSummaryStageStatus(t *testing.T) { kjs.Status.StageStatus = map[string]v1alpha1.JobStagePhase{ "bob": v1alpha1.JobCreateStageFailed, } - got = updateJobSummaryStageStatus(kj, kjs, domainIDs) + got = updateJobSummaryStageStatus(kj, kjs, domainIDs, true) assert.Equal(t, false, got) // stage status in job summary is empty but not empty in job, should return true @@ -351,28 +384,53 @@ func TestUpdateJobSummaryStageStatus(t *testing.T) { "bob": v1alpha1.JobCreateStageFailed, } kjs = makeMockJobSummary("bob", "job-1") - got = updateJobSummaryStageStatus(kj, kjs, domainIDs) + got = updateJobSummaryStageStatus(kj, kjs, domainIDs, true) assert.Equal(t, true, got) // stage status in job and job summary are different, should return true kj = makeMockJob("cross-domain", "job-1") kj.Status.StageStatus = map[string]v1alpha1.JobStagePhase{ - "bob": v1alpha1.JobStopStageSucceeded, + "alice": v1alpha1.JobStopStageSucceeded, + } + kjs = makeMockJobSummary("joke", "job-1") + kjs.Status.StageStatus = map[string]v1alpha1.JobStagePhase{ + "joke": v1alpha1.JobCreateStageSucceeded, + } + got = updateJobSummaryStageStatus(kj, kjs, domainIDs, true) + assert.Equal(t, true, got) + + // stage status in job and job summary are different for member, should return true + kj = makeMockJob("cross-domain", "job-1") + kj.Status.StageStatus = map[string]v1alpha1.JobStagePhase{ + "alice": v1alpha1.JobStopStageSucceeded, } kjs = makeMockJobSummary("bob", "job-1") kjs.Status.StageStatus = map[string]v1alpha1.JobStagePhase{ "bob": v1alpha1.JobCreateStageSucceeded, } - got = updateJobSummaryStageStatus(kj, kjs, domainIDs) + got = updateJobSummaryStageStatus(kj, kjs, domainIDs, true) assert.Equal(t, true, got) + + // stage status in job and job summary are same for member, should return false + kj = makeMockJob("cross-domain", "job-1") + kj.Status.StageStatus = map[string]v1alpha1.JobStagePhase{ + "bob": v1alpha1.JobCreateStageSucceeded, + } + kjs = makeMockJobSummary("bob", "job-1") + kjs.Status.StageStatus = map[string]v1alpha1.JobStagePhase{ + "bob": v1alpha1.JobCreateStageSucceeded, + } + got = updateJobSummaryStageStatus(kj, kjs, domainIDs, true) + assert.Equal(t, false, got) } func TestUpdateJobSummaryPartyTaskCreateStatus(t *testing.T) { + t.Parallel() domainIDs := []string{"bob"} // job party task create status is empty, should return false kj := makeMockJob("cross-domain", "job-1") kjs := makeMockJobSummary("bob", "job-1") - got := updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs) + got := updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs, true) assert.Equal(t, false, got) // party task create status in job and job summary are same, should return false @@ -390,7 +448,7 @@ func TestUpdateJobSummaryPartyTaskCreateStatus(t *testing.T) { Phase: v1alpha1.KusciaTaskCreateSucceeded, }}, } - got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs) + got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs, true) assert.Equal(t, false, got) // party task create status in job summary is empty but not empty in job, should return true @@ -402,7 +460,7 @@ func TestUpdateJobSummaryPartyTaskCreateStatus(t *testing.T) { }}, } kjs = makeMockJobSummary("bob", "job-1") - got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs) + got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs, true) assert.Equal(t, true, got) // party task create status in job and job summary are different, should return true @@ -413,18 +471,55 @@ func TestUpdateJobSummaryPartyTaskCreateStatus(t *testing.T) { Phase: v1alpha1.KusciaTaskCreateFailed, }}, } - kjs = makeMockJobSummary("bob", "job-1") + kjs = makeMockJobSummary("joke", "job-1") kjs.Status.PartyTaskCreateStatus = map[string][]v1alpha1.PartyTaskCreateStatus{ + "joke": {{ + DomainID: "joke", + Phase: v1alpha1.KusciaTaskCreateSucceeded, + }}, + } + got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs, true) + assert.Equal(t, true, got) + + // party task create status in job and job summary are different for member, should return true + kj = makeMockJob("cross-domain", "job-1") + kj.Status.PartyTaskCreateStatus = map[string][]v1alpha1.PartyTaskCreateStatus{ "bob": {{ DomainID: "bob", + Phase: v1alpha1.KusciaTaskCreateFailed, + }}, + } + kjs = makeMockJobSummary("bob", "job-1") + kjs.Status.PartyTaskCreateStatus = map[string][]v1alpha1.PartyTaskCreateStatus{ + "alice": {{ + DomainID: "alice", Phase: v1alpha1.KusciaTaskCreateSucceeded, }}, } - got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs) + got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs, false) assert.Equal(t, true, got) + + // party task create status in job and job summary are same for member, should return false + kj = makeMockJob("cross-domain", "job-1") + kj.Status.PartyTaskCreateStatus = map[string][]v1alpha1.PartyTaskCreateStatus{ + "bob": {{ + DomainID: "bob", + Phase: v1alpha1.KusciaTaskCreateSucceeded, + }}, + } + kjs = makeMockJobSummary("bob", "job-1") + kjs.Status.PartyTaskCreateStatus = map[string][]v1alpha1.PartyTaskCreateStatus{ + "bob": {{ + DomainID: "bob", + Phase: v1alpha1.KusciaTaskCreateSucceeded, + }}, + } + got = updateJobSummaryPartyTaskCreateStatus(kj, kjs, domainIDs, false) + assert.Equal(t, false, got) } func TestUpdateJobSummaryTaskStatus(t *testing.T) { + t.Parallel() // job task status is empty, should return false kj := makeMockJob("cross-domain", "job-1") kjs := makeMockJobSummary("bob", "job-1") @@ -457,8 +552,10 @@ func TestUpdateJobSummaryTaskStatus(t *testing.T) { } func TestProcessJobAsPartner(t *testing.T) { + t.Parallel() ctx := context.Background() - kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() + kj2 := makeMockJob("cross-domain", "job-2") + kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kj2) kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) deploymentInformer := kusciaInformerFactory.Kuscia().V1alpha1().KusciaDeployments() jobInformer := kusciaInformerFactory.Kuscia().V1alpha1().KusciaJobs() @@ -512,14 +609,14 @@ func TestProcessJobAsPartner(t *testing.T) { assert.Equal(t, nil, got) // job summary doesn't exist in host cluster, should return nil - kj = makeMockJob("cross-domain", "job-2") - kj.Annotations[common.InitiatorAnnotationKey] = "alice" - kj.Annotations[common.KusciaPartyMasterDomainAnnotationKey] = "bob" - got = c.processJobAsPartner(ctx, kj) + kj2.Annotations[common.InitiatorAnnotationKey] = "alice" + kj2.Annotations[common.KusciaPartyMasterDomainAnnotationKey] = "bob" + got = c.processJobAsPartner(ctx, kj2) assert.Equal(t, nil, got) } func TestUpdateHostJobSummary(t *testing.T) { + t.Parallel() ctx := context.Background() kjs := makeMockJobSummary("bob", "job-1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kjs) diff --git a/pkg/interconn/kuscia/jobsummary.go b/pkg/interconn/kuscia/jobsummary.go index a6cef713..69ecc7b5 100644 --- a/pkg/interconn/kuscia/jobsummary.go +++ b/pkg/interconn/kuscia/jobsummary.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( @@ -95,12 +95,23 @@ func (c *Controller) updateJob(ctx context.Context, jobSummary *v1alpha1.KusciaJ if err != nil { nlog.Errorf("Failed to get job %v, %v", jobSummary.Name, err) if k8serrors.IsNotFound(err) { + nlog.Infof("Job %v is not found, delete party mirror job and jobSummary %v", jobSummary.Name, ikcommon.GetObjectNamespaceName(jobSummary)) + if err = c.kusciaClient.KusciaV1alpha1().KusciaJobSummaries(jobSummary.Namespace).Delete(ctx, jobSummary.Name, metav1.DeleteOptions{}); err != nil && !k8serrors.IsNotFound(err) { + return err + } + if err = c.kusciaClient.KusciaV1alpha1().KusciaJobs(jobSummary.Namespace).Delete(ctx, jobSummary.Name, metav1.DeleteOptions{}); err != nil && !k8serrors.IsNotFound(err) { + return err + } return nil } return err } } + if originalJob.Status.CompletionTime != nil { + return nil + } + partyDomainIDs := ikcommon.GetInterConnKusciaPartyDomainIDs(jobSummary) if partyDomainIDs == nil { nlog.Warnf("Failed to get interconn kuscia party domain ids from jobSummary %v, skip processing it", ikcommon.GetObjectNamespaceName(jobSummary)) @@ -109,7 +120,7 @@ func (c *Controller) updateJob(ctx context.Context, jobSummary *v1alpha1.KusciaJ job := originalJob.DeepCopy() needUpdate := false - if updated := ikcommon.UpdateJobStage(job, jobSummary); updated { + if ikcommon.UpdateJobStage(job, jobSummary) { job.Status.LastReconcileTime = ikcommon.GetCurrentTime() if _, err = c.kusciaClient.KusciaV1alpha1().KusciaJobs(job.Namespace).Update(ctx, job, metav1.UpdateOptions{}); err != nil { return err @@ -117,7 +128,11 @@ func (c *Controller) updateJob(ctx context.Context, jobSummary *v1alpha1.KusciaJ return nil } - if updated := updateJobStatusInfo(job, jobSummary, partyDomainIDs); updated { + if updateJobStatusInfo(job, jobSummary, partyDomainIDs) { + needUpdate = true + } + + if updateJobStatusPhase(job, jobSummary) { needUpdate = true } @@ -130,6 +145,23 @@ func (c *Controller) updateJob(ctx context.Context, jobSummary *v1alpha1.KusciaJ return nil } +func updateJobStatusPhase(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary) bool { + if jobSummary.Status.Phase == v1alpha1.KusciaJobFailed && + jobSummary.Status.Phase != job.Status.Phase && + jobSummary.Status.CompletionTime != nil && + jobSummary.Status.Reason != "" && + jobSummary.Status.Reason != job.Status.Reason { + switch jobSummary.Status.Reason { + case string(v1alpha1.ValidateFailed), string(v1alpha1.CreateTaskFailed): + job.Status.Phase = jobSummary.Status.Phase + job.Status.Reason = jobSummary.Status.Reason + job.Status.Message = jobSummary.Status.Message + return true + } + } + return false +} + func updateJobStatusInfo(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJobSummary, domainIDs []string) bool { updated := false if job.Status.ApproveStatus == nil && len(jobSummary.Status.ApproveStatus) > 0 { @@ -163,5 +195,6 @@ func updateJobStatusInfo(job *v1alpha1.KusciaJob, jobSummary *v1alpha1.KusciaJob updated = true } } + return updated } diff --git a/pkg/interconn/kuscia/jobsummary_test.go b/pkg/interconn/kuscia/jobsummary_test.go index e6b9abf8..4a33c2cb 100644 --- a/pkg/interconn/kuscia/jobsummary_test.go +++ b/pkg/interconn/kuscia/jobsummary_test.go @@ -41,6 +41,7 @@ func makeMockJobSummary(namespace, name string) *v1alpha1.KusciaJobSummary { } func TestHandleUpdatedJobSummary(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -88,6 +89,7 @@ func TestHandleUpdatedJobSummary(t *testing.T) { } func TestUpdateJob(t *testing.T) { + t.Parallel() ctx := context.Background() kj := makeMockJob("cross-domain", "job-1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kj) @@ -101,10 +103,10 @@ func TestUpdateJob(t *testing.T) { jobLister: jobInformer.Lister(), } - // job doesn't exist, should return nil + // job doesn't exist, delete mirror job and job summary, should return nil kjs := makeMockJobSummary("bob", "job-2") got := c.updateJob(ctx, kjs) - assert.Equal(t, nil, got) + assert.Equal(t, true, got == nil) // job party domain ids is empty, should return nil kjs = makeMockJobSummary("bob", "job-1") @@ -120,6 +122,7 @@ func TestUpdateJob(t *testing.T) { } func TestUpdateJobStatusInfo(t *testing.T) { + t.Parallel() domainIDs := []string{"bob"} // all status in job and job summary are empty, should return false kj := makeMockJob("cross-domain", "job-1") diff --git a/pkg/interconn/kuscia/task.go b/pkg/interconn/kuscia/task.go index 9886cac0..2920990e 100644 --- a/pkg/interconn/kuscia/task.go +++ b/pkg/interconn/kuscia/task.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( @@ -145,7 +145,7 @@ func (c *Controller) processTaskAsInitiator(ctx context.Context, task *v1alpha1. return c.createOrUpdateTaskSummary(ctx, task) } -// createOrUpdateJobSummary creates or updates job summary. +// createOrUpdateTaskSummary creates or updates job summary. func (c *Controller) createOrUpdateTaskSummary(ctx context.Context, task *v1alpha1.KusciaTask) error { masterDomains, err := ikcommon.GetPartyMasterDomains(c.domainLister, task) if err != nil { @@ -166,6 +166,9 @@ func (c *Controller) createOrUpdateTaskSummary(ctx context.Context, task *v1alph return err } + if taskSummary.Status.CompletionTime != nil { + continue + } // update taskSummary if err = c.updateTaskSummaryByTask(ctx, task, taskSummary.DeepCopy()); err != nil { return err @@ -205,23 +208,19 @@ func (c *Controller) createTaskSummary(ctx context.Context, task *v1alpha1.Kusci } func (c *Controller) updateTaskSummaryByTask(ctx context.Context, task *v1alpha1.KusciaTask, taskSummary *v1alpha1.KusciaTaskSummary) error { - domainIDs := ikcommon.GetSelfClusterPartyDomainIDs(task) - if domainIDs == nil { - nlog.Warnf("failed to get self cluster party domain ids from task %v, skip processing it", ikcommon.GetObjectNamespaceName(task)) - return nil - } - now := ikcommon.GetCurrentTime() needUpdate := false if task.Status.Phase != taskSummary.Status.Phase { needUpdate = true taskSummary.Status.Phase = task.Status.Phase - if taskSummary.Status.Phase == v1alpha1.TaskSucceeded { - taskSummary.Status.CompletionTime = now - } } - if updateTaskSummaryPartyTaskStatus(task, taskSummary, domainIDs) { + if task.Status.CompletionTime != nil && taskSummary.Status.CompletionTime == nil { + needUpdate = true + taskSummary.Status.CompletionTime = task.Status.CompletionTime + } + + if updateTaskSummaryPartyTaskStatus(task, taskSummary, true) { needUpdate = true } @@ -236,7 +235,7 @@ func (c *Controller) updateTaskSummaryByTask(ctx context.Context, task *v1alpha1 return nil } -func updateTaskSummaryPartyTaskStatus(task *v1alpha1.KusciaTask, taskSummary *v1alpha1.KusciaTaskSummary, domainIDs []string) bool { +func updateTaskSummaryPartyTaskStatus(task *v1alpha1.KusciaTask, taskSummary *v1alpha1.KusciaTaskSummary, isHost bool) bool { if len(task.Status.PartyTaskStatus) == 0 || reflect.DeepEqual(task.Status.PartyTaskStatus, taskSummary.Status.PartyTaskStatus) { return false } @@ -246,36 +245,53 @@ func updateTaskSummaryPartyTaskStatus(task *v1alpha1.KusciaTask, taskSummary *v1 return true } - var partyTaskStatusInTask []v1alpha1.PartyTaskStatus - for _, domainID := range domainIDs { - for i, pts := range task.Status.PartyTaskStatus { - if domainID == pts.DomainID { - partyTaskStatusInTask = append(partyTaskStatusInTask, task.Status.PartyTaskStatus[i]) - } - } + ptsInTaskSummaryMap := make(map[string]v1alpha1.PartyTaskStatus) + for _, pts := range taskSummary.Status.PartyTaskStatus { + key := fmt.Sprintf("%s:%s", pts.DomainID, pts.Role) + ptsInTaskSummaryMap[key] = pts } updated := false - for i, pts := range partyTaskStatusInTask { - found := false - for j, jsPts := range taskSummary.Status.PartyTaskStatus { - if pts.DomainID == jsPts.DomainID && pts.Role == jsPts.Role { - found = true - if pts.Phase != jsPts.Phase { - taskSummary.Status.PartyTaskStatus[j].Phase = pts.Phase - updated = true - } + if isHost { + for _, pts := range task.Status.PartyTaskStatus { + if pts.DomainID == taskSummary.Namespace { + continue + } + key := fmt.Sprintf("%s:%s", pts.DomainID, pts.Role) + if !reflect.DeepEqual(ptsInTaskSummaryMap[key], pts) { + updated = true + ptsInTaskSummaryMap[key] = pts + } + } + } else { + domainIDs := ikcommon.GetSelfClusterPartyDomainIDs(task) + if len(domainIDs) == 0 { + nlog.Warnf("Failed to get self cluster party domain ids from task %v, skip updating host task summary party task status", ikcommon.GetObjectNamespaceName(task)) + return false + } - if pts.Message != jsPts.Message { - taskSummary.Status.PartyTaskStatus[j].Message = pts.Message - updated = true + ptsInTaskMap := make(map[string]v1alpha1.PartyTaskStatus) + for _, domainID := range domainIDs { + for _, pts := range task.Status.PartyTaskStatus { + if domainID == pts.DomainID { + key := fmt.Sprintf("%s:%s", pts.DomainID, pts.Role) + ptsInTaskMap[key] = pts } } } - if !found { - taskSummary.Status.PartyTaskStatus = append(taskSummary.Status.PartyTaskStatus, partyTaskStatusInTask[i]) - updated = true + for key, pts := range ptsInTaskMap { + if !reflect.DeepEqual(ptsInTaskSummaryMap[key], pts) { + updated = true + ptsInTaskSummaryMap[key] = pts + } + } + } + + if updated { + taskSummary.Status.PartyTaskStatus = nil + for _, status := range ptsInTaskSummaryMap { + taskSummary.Status.PartyTaskStatus = append(taskSummary.Status.PartyTaskStatus, status) } } @@ -319,12 +335,20 @@ func (c *Controller) processTaskAsPartner(ctx context.Context, task *v1alpha1.Ku if err != nil { nlog.Errorf("Failed to get taskSummary %v from host %v cluster, %v", task.Name, initiator, err) if k8serrors.IsNotFound(err) { - return nil + nlog.Infof("Host taskSummary %s/%s is not found, delete task %v", masterDomainID, task.Name, ikcommon.GetObjectNamespaceName(task)) + err = c.kusciaClient.KusciaV1alpha1().KusciaTasks(task.Namespace).Delete(ctx, task.Name, metav1.DeleteOptions{}) + if k8serrors.IsNotFound(err) { + return nil + } } return err } } + if hTaskSummary.Status.CompletionTime != nil { + return nil + } + return c.updateHostTaskSummary(ctx, hra.HostKusciaClient(), task, hTaskSummary.DeepCopy()) } @@ -333,14 +357,8 @@ func (c *Controller) updateHostTaskSummary(ctx context.Context, task *v1alpha1.KusciaTask, taskSummary *v1alpha1.KusciaTaskSummary) error { - domainIDs := ikcommon.GetSelfClusterPartyDomainIDs(task) - if domainIDs == nil { - nlog.Warnf("Failed to get self cluster party domain ids from task %v, skip processing it", ikcommon.GetObjectNamespaceName(task)) - return nil - } - needUpdate := false - if updateHostTaskSummaryPartyTaskStatus(task, taskSummary, domainIDs) { + if updateTaskSummaryPartyTaskStatus(task, taskSummary, false) { needUpdate = true } @@ -354,7 +372,3 @@ func (c *Controller) updateHostTaskSummary(ctx context.Context, return nil } - -func updateHostTaskSummaryPartyTaskStatus(task *v1alpha1.KusciaTask, taskSummary *v1alpha1.KusciaTaskSummary, domainIDs []string) bool { - return updateTaskSummaryPartyTaskStatus(task, taskSummary, domainIDs) -} diff --git a/pkg/interconn/kuscia/task_test.go b/pkg/interconn/kuscia/task_test.go index efa28330..9a0f595f 100644 --- a/pkg/interconn/kuscia/task_test.go +++ b/pkg/interconn/kuscia/task_test.go @@ -43,6 +43,7 @@ func makeMockTask(namespace, name string) *v1alpha1.KusciaTask { } func TestHandleUpdatedTask(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -90,6 +91,7 @@ func TestHandleUpdatedTask(t *testing.T) { } func TestHandleDeletedTask(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) domainInformer := kusciaInformerFactory.Kuscia().V1alpha1().Domains() @@ -135,6 +137,7 @@ func TestHandleDeletedTask(t *testing.T) { } func TestDeleteTaskCascadedResources(t *testing.T) { + t.Parallel() kts := makeMockTaskSummary("bob", "task-1", "1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kts) kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -150,6 +153,7 @@ func TestDeleteTaskCascadedResources(t *testing.T) { } func TestCreateOrUpdateTaskSummary(t *testing.T) { + t.Parallel() ctx := context.Background() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) @@ -189,6 +193,7 @@ func TestCreateOrUpdateTaskSummary(t *testing.T) { } func TestUpdateTaskSummaryByTask(t *testing.T) { + t.Parallel() ctx := context.Background() kts := makeMockTaskSummary("bob", "task-1", "1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kts) @@ -215,16 +220,18 @@ func TestUpdateTaskSummaryByTask(t *testing.T) { } func TestUpdateTaskSummaryPartyTaskStatus(t *testing.T) { - domainIDs := []string{"alice", "bob"} + t.Parallel() // party task status is empty in task status, should return false kt := makeMockTask("cross-domain", "task-1") + kt.Annotations = map[string]string{common.InterConnSelfPartyAnnotationKey: "alice_bob"} kts := makeMockTaskSummary("bob", "task-1", "1") - got := updateTaskSummaryPartyTaskStatus(kt, kts, domainIDs) + got := updateTaskSummaryPartyTaskStatus(kt, kts, true) assert.Equal(t, false, got) // party task status is same for task and task summary, should return false kt = makeMockTask("cross-domain", "task-1") + kt.Annotations = map[string]string{common.InterConnSelfPartyAnnotationKey: "alice_bob"} kt.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ DomainID: "bob", Phase: v1alpha1.TaskRunning, @@ -234,35 +241,38 @@ func TestUpdateTaskSummaryPartyTaskStatus(t *testing.T) { DomainID: "bob", Phase: v1alpha1.TaskRunning, }} - got = updateTaskSummaryPartyTaskStatus(kt, kts, domainIDs) + got = updateTaskSummaryPartyTaskStatus(kt, kts, true) assert.Equal(t, false, got) // party task status is empty in task summary but not empty for task, should return true kt = makeMockTask("cross-domain", "task-1") + kt.Annotations = map[string]string{common.InterConnSelfPartyAnnotationKey: "alice_bob"} kt.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ DomainID: "bob", Phase: v1alpha1.TaskRunning, }} kts = makeMockTaskSummary("bob", "task-1", "1") - got = updateTaskSummaryPartyTaskStatus(kt, kts, domainIDs) + got = updateTaskSummaryPartyTaskStatus(kt, kts, true) assert.Equal(t, true, got) // party task status is different in task summary and task, should return true kt = makeMockTask("cross-domain", "task-1") + kt.Annotations = map[string]string{common.InterConnSelfPartyAnnotationKey: "alice_bob"} kt.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ DomainID: "bob", Phase: v1alpha1.TaskRunning, }} - kts = makeMockTaskSummary("bob", "task-1", "1") + kts = makeMockTaskSummary("joke", "task-1", "1") kts.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ - DomainID: "bob", + DomainID: "joke", Phase: v1alpha1.TaskFailed, }} - got = updateTaskSummaryPartyTaskStatus(kt, kts, domainIDs) + got = updateTaskSummaryPartyTaskStatus(kt, kts, true) assert.Equal(t, true, got) // party task status exist in task but not in task summary, should return true kt = makeMockTask("cross-domain", "task-1") + kt.Annotations = map[string]string{common.InterConnSelfPartyAnnotationKey: "alice_bob"} kt.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ DomainID: "alice", Phase: v1alpha1.TaskRunning, @@ -272,13 +282,45 @@ func TestUpdateTaskSummaryPartyTaskStatus(t *testing.T) { DomainID: "bob", Phase: v1alpha1.TaskFailed, }} - got = updateTaskSummaryPartyTaskStatus(kt, kts, domainIDs) + got = updateTaskSummaryPartyTaskStatus(kt, kts, true) assert.Equal(t, true, got) + + // party task status is different in task summary and task for member, should return true + kt = makeMockTask("cross-domain", "task-1") + kt.Annotations = map[string]string{common.InterConnSelfPartyAnnotationKey: "alice_bob"} + kt.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ + DomainID: "bob", + Phase: v1alpha1.TaskFailed, + }} + kts = makeMockTaskSummary("bob", "task-1", "1") + kts.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ + DomainID: "bob", + Phase: v1alpha1.TaskRunning, + }} + got = updateTaskSummaryPartyTaskStatus(kt, kts, false) + assert.Equal(t, true, got) + + // party task status is same in task summary and task for member, should return true + kt = makeMockTask("cross-domain", "task-1") + kt.Annotations = map[string]string{common.InterConnSelfPartyAnnotationKey: "alice_bob"} + kt.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ + DomainID: "bob", + Phase: v1alpha1.TaskFailed, + }} + kts = makeMockTaskSummary("bob", "task-1", "1") + kts.Status.PartyTaskStatus = []v1alpha1.PartyTaskStatus{{ + DomainID: "bob", + Phase: v1alpha1.TaskFailed, + }} + got = updateTaskSummaryPartyTaskStatus(kt, kts, false) + assert.Equal(t, false, got) } func TestProcessTaskAsPartner(t *testing.T) { + t.Parallel() ctx := context.Background() - kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() + kt2 := makeMockTask("cross-domain", "task-2") + kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kt2) kusciaInformerFactory := kusciainformers.NewSharedInformerFactory(kusciaFakeClient, 0) deploymentInformer := kusciaInformerFactory.Kuscia().V1alpha1().KusciaDeployments() jobInformer := kusciaInformerFactory.Kuscia().V1alpha1().KusciaJobs() @@ -331,15 +373,15 @@ func TestProcessTaskAsPartner(t *testing.T) { got = c.processTaskAsPartner(ctx, kt) assert.Equal(t, nil, got) - // task summary doesn't exist in host cluster, should return nil - kt = makeMockTask("cross-domain", "task-2") + // task summary doesn't exist in host cluster, delete task and should return nil kt.Annotations[common.InitiatorAnnotationKey] = "alice" kt.Annotations[common.KusciaPartyMasterDomainAnnotationKey] = "bob" - got = c.processTaskAsPartner(ctx, kt) - assert.Equal(t, nil, got) + got = c.processTaskAsPartner(ctx, kt2) + assert.Equal(t, true, got == nil) } func TestUpdateHostTaskSummary(t *testing.T) { + t.Parallel() ctx := context.Background() kts := makeMockTaskSummary("bob", "task-1", "1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kts) diff --git a/pkg/interconn/kuscia/taskresource.go b/pkg/interconn/kuscia/taskresource.go index 0c156e67..2924e1a4 100644 --- a/pkg/interconn/kuscia/taskresource.go +++ b/pkg/interconn/kuscia/taskresource.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( diff --git a/pkg/interconn/kuscia/taskresource_test.go b/pkg/interconn/kuscia/taskresource_test.go index 8cc22d96..77f01ecc 100644 --- a/pkg/interconn/kuscia/taskresource_test.go +++ b/pkg/interconn/kuscia/taskresource_test.go @@ -43,6 +43,7 @@ func makeMockTaskResource(namespace, name string) *v1alpha1.TaskResource { } func TestHandleUpdatedTaskResource(t *testing.T) { + t.Parallel() kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -90,6 +91,7 @@ func TestHandleUpdatedTaskResource(t *testing.T) { } func TestUpdateTaskSummaryByTaskResource(t *testing.T) { + t.Parallel() ctx := context.Background() kts := makeMockTaskSummary("bob", "task-1", "1") kusciaFakeClient := kusciaclientsetfake.NewSimpleClientset(kts) @@ -194,6 +196,7 @@ func TestUpdateTaskSummaryByTaskResource(t *testing.T) { } func TestUpdateHostTaskSummaryByTaskResource(t *testing.T) { + t.Parallel() ctx := context.Background() kts := makeMockTaskSummary("bob", "task-1", "1") diff --git a/pkg/interconn/kuscia/tasksummary.go b/pkg/interconn/kuscia/tasksummary.go index b4df7ee6..72191915 100644 --- a/pkg/interconn/kuscia/tasksummary.go +++ b/pkg/interconn/kuscia/tasksummary.go @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package kuscia import ( "context" - "fmt" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -67,7 +66,7 @@ func (c *Controller) handleUpdatedTaskSummary(oldObj, newObj interface{}) { // syncTaskSummaryHandler compares the actual state with the desired, and attempts to // converge the two. It then updates the Status block of the resource // with the current status of the resource. -func (c *Controller) syncTaskSummaryHandler(ctx context.Context, key string) (err error) { +func (c *Controller) syncTaskSummaryHandler(ctx context.Context, key string) error { namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { nlog.Errorf("Failed to split taskSummary key %v, %v, skip processing it", key, err) @@ -91,43 +90,53 @@ func (c *Controller) syncTaskSummaryHandler(ctx context.Context, key string) (er } taskSummary := originalTs.DeepCopy() - if updateErr := c.updateTask(ctx, taskSummary, partyDomainIDs); updateErr != nil { - err = updateErr + if shouldReturn, err := c.updateTask(ctx, taskSummary, partyDomainIDs); shouldReturn || err != nil { + return err } - if updateErr := c.updateTaskResource(ctx, taskSummary, partyDomainIDs); updateErr != nil { - err = updateErr + if err = c.updateTaskResource(ctx, taskSummary, partyDomainIDs); err != nil { + return err } - return err } -func (c *Controller) updateTask(ctx context.Context, taskSummary *v1alpha1.KusciaTaskSummary, domainIDs []string) error { +func (c *Controller) updateTask(ctx context.Context, taskSummary *v1alpha1.KusciaTaskSummary, domainIDs []string) (bool, error) { originalTask, err := c.taskLister.KusciaTasks(common.KusciaCrossDomain).Get(taskSummary.Name) if err != nil { - return err + if k8serrors.IsNotFound(err) { + originalTask, err = c.kusciaClient.KusciaV1alpha1().KusciaTasks(common.KusciaCrossDomain).Get(ctx, taskSummary.Name, metav1.GetOptions{}) + } + if err != nil { + if k8serrors.IsNotFound(err) { + nlog.Infof("Task %v is not found, delete taskSummary %v", taskSummary.Name, ikcommon.GetObjectNamespaceName(taskSummary)) + err = c.kusciaClient.KusciaV1alpha1().KusciaTaskSummaries(taskSummary.Namespace).Delete(ctx, taskSummary.Name, metav1.DeleteOptions{}) + if k8serrors.IsNotFound(err) { + return true, nil + } + return true, err + } + } + nlog.Errorf("Failed to get task %v, %v", taskSummary.Name, err) + return true, err } - tsRvInTask := ikcommon.GetObjectAnnotation(originalTask, common.TaskSummaryResourceVersionAnnotationKey) - if !utilsres.CompareResourceVersion(taskSummary.ResourceVersion, tsRvInTask) { - nlog.Infof("TaskSummary resource version is not greater than the annotation value in task %v, skip updating task", originalTask.Name) - return nil + if taskSummary.Status.CompletionTime != nil { + return true, nil } task := originalTask.DeepCopy() needUpdate := false if updated := updateTaskPartyStatus(task, taskSummary, domainIDs); updated { - task.Annotations[common.TaskSummaryResourceVersionAnnotationKey] = taskSummary.ResourceVersion needUpdate = true } if needUpdate { task.Status.LastReconcileTime = ikcommon.GetCurrentTime() if _, err = c.kusciaClient.KusciaV1alpha1().KusciaTasks(task.Namespace).UpdateStatus(ctx, task, metav1.UpdateOptions{}); err != nil { - return err + return true, err } } - return nil + return false, nil } func updateTaskPartyStatus(task *v1alpha1.KusciaTask, taskSummary *v1alpha1.KusciaTaskSummary, domainIDs []string) bool { @@ -184,9 +193,18 @@ func (c *Controller) updateTaskResource(ctx context.Context, taskSummary *v1alph } for _, status := range statuses { + if status.HostTaskResourceName == "" { + continue + } originalTr, err := c.taskResourceLister.TaskResources(domainID).Get(status.HostTaskResourceName) if err != nil { - return fmt.Errorf("failed to update taskResource based on taskSummary %v, %v", ikcommon.GetObjectNamespaceName(taskSummary), err) + if k8serrors.IsNotFound(err) { + originalTr, err = c.kusciaClient.KusciaV1alpha1().TaskResources(domainID).Get(ctx, status.HostTaskResourceName, metav1.GetOptions{}) + } + if err != nil { + nlog.Errorf("Failed to get taskResource based on taskSummary %v, %v", ikcommon.GetObjectNamespaceName(taskSummary), err) + return err + } } tsRvInTaskResource := ikcommon.GetObjectAnnotation(originalTr, common.TaskSummaryResourceVersionAnnotationKey) diff --git a/pkg/interconn/kuscia/tasksummary_test.go b/pkg/interconn/kuscia/tasksummary_test.go index 99306139..bc455dd1 100644 --- a/pkg/interconn/kuscia/tasksummary_test.go +++ b/pkg/interconn/kuscia/tasksummary_test.go @@ -54,6 +54,7 @@ func makeMockTaskSummary(namespace, name, rv string) *v1alpha1.KusciaTaskSummary } func TestHandleUpdatedTaskSummary(t *testing.T) { + t.Parallel() kusciaFakeClient := fake.NewSimpleClientset() c := NewController(context.Background(), nil, kusciaFakeClient, nil) if c == nil { @@ -99,6 +100,7 @@ func TestHandleUpdatedTaskSummary(t *testing.T) { } func TestUpdateTask(t *testing.T) { + t.Parallel() ctx := context.Background() kt1 := makeMockTask("cross-domain", "task-1") @@ -143,7 +145,7 @@ func TestUpdateTask(t *testing.T) { name: "can't find task by taskSummary", taskSummary: ts3, domainIDs: []string{"bob"}, - wantErr: true, + wantErr: false, }, { name: "update task by taskSummary", @@ -155,13 +157,14 @@ func TestUpdateTask(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := c.updateTask(ctx, tt.taskSummary, tt.domainIDs) + _, got := c.updateTask(ctx, tt.taskSummary, tt.domainIDs) assert.Equal(t, tt.wantErr, got != nil) }) } } func TestUpdateTaskPartyStatus(t *testing.T) { + t.Parallel() // partyTaskStatus in taskSummary is empty, should return false. domainIDs := []string{"bob"} kt := makeMockTask("cross-domain", "task-1") @@ -205,6 +208,7 @@ func TestUpdateTaskPartyStatus(t *testing.T) { } func TestUpdateTaskResource(t *testing.T) { + t.Parallel() ctx := context.Background() tr1 := makeMockTaskResource("bob", "tr-1") tr1.Annotations[common.TaskSummaryResourceVersionAnnotationKey] = "1" diff --git a/pkg/kusciaapi/bean/grpc_server_bean.go b/pkg/kusciaapi/bean/grpc_server_bean.go index 35291b0b..639322b8 100644 --- a/pkg/kusciaapi/bean/grpc_server_bean.go +++ b/pkg/kusciaapi/bean/grpc_server_bean.go @@ -21,13 +21,12 @@ import ( "net" "time" - "github.com/secretflow/kuscia/pkg/common" - cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" - "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/reflection" + "github.com/secretflow/kuscia/pkg/common" + cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" "github.com/secretflow/kuscia/pkg/kusciaapi/config" "github.com/secretflow/kuscia/pkg/kusciaapi/handler/grpchandler" "github.com/secretflow/kuscia/pkg/kusciaapi/service" @@ -38,6 +37,7 @@ import ( "github.com/secretflow/kuscia/pkg/web/framework" frameworkconfig "github.com/secretflow/kuscia/pkg/web/framework/config" "github.com/secretflow/kuscia/pkg/web/interceptor" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -63,6 +63,8 @@ func (s *grpcServerBean) Start(ctx context.Context, e framework.ConfBeanRegistry // init grpc server opts opts := []grpc.ServerOption{ grpc.ConnectionTimeout(time.Duration(s.config.ConnectTimeout) * time.Second), + grpc.ChainUnaryInterceptor(interceptor.UnaryRecoverInterceptor(pberrorcode.ErrorCode_KusciaAPIErrForUnexpected)), + grpc.StreamInterceptor(interceptor.StreamRecoverInterceptor(pberrorcode.ErrorCode_KusciaAPIErrForUnexpected)), } if s.config.TLS != nil { serverTLSConfig, err := buildServerTLSConfig(s.config.TLS, s.config.Protocol) @@ -79,7 +81,7 @@ func (s *grpcServerBean) Start(ctx context.Context, e framework.ConfBeanRegistry if err != nil { nlog.Fatalf("failed to listen on addr[%s]: %v", addr, err) } - + opts = append(opts, grpc.ChainUnaryInterceptor(interceptor.GrpcServerLoggingInterceptor(*s.config.InterceptorLog))) // set token auth interceptor tokenConfig := s.config.Token if s.config.Token != nil { @@ -92,7 +94,6 @@ func (s *grpcServerBean) Start(ctx context.Context, e framework.ConfBeanRegistry tokenStreamInterceptor := grpc.ChainStreamInterceptor(interceptor.GrpcStreamServerTokenInterceptor(token)) opts = append(opts, tokenStreamInterceptor) } - // set master role interceptor opts = append(opts, grpc.ChainUnaryInterceptor(interceptor.GrpcServerMasterRoleInterceptor())) opts = append(opts, grpc.ChainStreamInterceptor(interceptor.GrpcStreamServerMasterRoleInterceptor())) diff --git a/pkg/kusciaapi/bean/http_server_bean.go b/pkg/kusciaapi/bean/http_server_bean.go index 2c255072..19e7beb5 100644 --- a/pkg/kusciaapi/bean/http_server_bean.go +++ b/pkg/kusciaapi/bean/http_server_bean.go @@ -12,21 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package bean import ( "context" "crypto/rsa" + "fmt" "net/http" "sync/atomic" "github.com/gin-gonic/gin" + "google.golang.org/protobuf/encoding/protojson" "github.com/secretflow/kuscia/pkg/common" "github.com/secretflow/kuscia/pkg/confmanager/handler/httphandler/certificate" cmservice "github.com/secretflow/kuscia/pkg/confmanager/service" - ecode "github.com/secretflow/kuscia/pkg/datamesh/errorcode" apiconfig "github.com/secretflow/kuscia/pkg/kusciaapi/config" "github.com/secretflow/kuscia/pkg/kusciaapi/handler/httphandler/domain" "github.com/secretflow/kuscia/pkg/kusciaapi/handler/httphandler/domaindata" @@ -49,6 +50,8 @@ import ( frameworkconfig "github.com/secretflow/kuscia/pkg/web/framework/config" "github.com/secretflow/kuscia/pkg/web/framework/router" "github.com/secretflow/kuscia/pkg/web/interceptor" + "github.com/secretflow/kuscia/proto/api/v1alpha1" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) type httpServerBean struct { @@ -106,7 +109,7 @@ func (s *httpServerBean) externalGinInit(e framework.ConfBeanRegistry) error { return err } // recover middleware - s.externalGinBean.Use(gin.Recovery()) + s.externalGinBean.Use(gin.Recovery(), interceptor.HTTPServerLoggingInterceptor(*s.config.InterceptorLog)) // auth token tokenConfig := s.config.Token if tokenConfig != nil { @@ -476,7 +479,25 @@ func newCertService(config *apiconfig.KusciaAPIConfig) cmservice.ICertificateSer // protoDecorator is used to wrap handler. func protoDecorator(e framework.ConfBeanRegistry, handler api.ProtoHandler) gin.HandlerFunc { - return decorator.InterConnProtoDecoratorMaker(int32(ecode.ErrRequestInvalidate), int32(ecode.ErrForUnexpected))(e, handler) + return decorator.CustomProtoDecoratorMaker(setKusciaAPIErrorResp(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate), setKusciaAPIErrorResp(pberrorcode.ErrorCode_KusciaAPIErrForUnexpected))(e, handler) +} + +func setKusciaAPIErrorResp(errCode pberrorcode.ErrorCode) func(flow *decorator.BizFlow, errs *errorcode.Errs) (response api.ProtoResponse) { + return func(flow *decorator.BizFlow, errs *errorcode.Errs) (response api.ProtoResponse) { + + wrappedErr := fmt.Errorf("%s", errs) + resp := v1alpha1.ErrorResponse{ + Status: &v1alpha1.Status{ + Code: int32(errCode), + Message: wrappedErr.Error(), + }, + } + bytes, _ := protojson.Marshal(&resp) + response = &api.AnyStringProto{ + Content: string(bytes), + } + return response + } } func convertToGinConf(conf *apiconfig.KusciaAPIConfig) beans.GinBeanConfig { diff --git a/pkg/kusciaapi/config/kusciaapi_config.go b/pkg/kusciaapi/config/kusciaapi_config.go index c4caa813..a9f66d3b 100644 --- a/pkg/kusciaapi/config/kusciaapi_config.go +++ b/pkg/kusciaapi/config/kusciaapi_config.go @@ -24,6 +24,7 @@ import ( "github.com/secretflow/kuscia/pkg/common" kusciaclientset "github.com/secretflow/kuscia/pkg/crd/clientset/versioned" + "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/web/framework/config" ) @@ -49,6 +50,7 @@ type KusciaAPIConfig struct { DomainCertValue *atomic.Value `yaml:"-"` ConfDir string `yaml:"-"` DomainID string `yaml:"-"` + InterceptorLog *nlog.NLog `yaml:"-"` } type TokenConfig struct { diff --git a/pkg/kusciaapi/errorcode/error_code.go b/pkg/kusciaapi/errorcode/error_code.go index 3fe78495..ccb86a1b 100644 --- a/pkg/kusciaapi/errorcode/error_code.go +++ b/pkg/kusciaapi/errorcode/error_code.go @@ -18,152 +18,85 @@ package errorcode import ( "k8s.io/apimachinery/pkg/api/errors" - "github.com/secretflow/kuscia/pkg/web/errorcode" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) -const ( - ErrRequestValidate = 11100 - ErrForUnexpected = 11101 - ErrAuthFailed = 11102 - ErrRequestMasterFailed = 11103 - ErrLiteAPINotSupport = 11104 - - ErrCreateJob = 11201 - ErrQueryJob = 11202 - ErrQueryJobStatus = 11203 - ErrDeleteJob = 11204 - ErrStopJob = 11205 - ErrApproveJob = 11206 - ErrSuspendJob = 11207 - ErrRestartJob = 11208 - ErrCancelJob = 11209 - ErrSuspendNotRunningJob = 11210 - ErrRestartNotSuspendedOrFailedJob = 11211 - - ErrCreateDomain = 11300 - ErrQueryDomain = 11301 - ErrQueryDomainStatus = 11302 - ErrUpdateDomain = 11303 - ErrDeleteDomain = 11304 - ErrDomainNotExists = 11305 - ErrDomainExists = 11306 - - ErrCreateDomainRoute = 11400 - ErrQueryDomainRoute = 11401 - ErrQueryDomainRouteStatus = 11402 - ErrDeleteDomainRoute = 11403 - ErrDomainRouteNotExists = 11404 - ErrDomainRouteExists = 11405 - - ErrCreateDomainDataFailed = 11500 - ErrDeleteDomainDataFailed = 11501 - ErrGetDomainDataFailed = 11502 - ErrListDomainDataFailed = 11503 - ErrMergeDomainDataFailed = 11504 - ErrPatchDomainDataFailed = 11505 - ErrDomainDataNotExists = 11506 - ErrDomainDataExists = 11507 - - ErrCreateServing = 11600 - ErrQueryServing = 11601 - ErrQueryServingStatus = 11602 - ErrUpdateServing = 11603 - ErrDeleteServing = 11604 - - ErrCreateDomainDataGrant = 11700 - ErrUpdateDomainDataGrant = 11701 - ErrQueryDomainDataGrant = 11702 - ErrDeleteDomainDataGrant = 11703 - ErrDomainDataGrantExists = 11704 - ErrDomainDataGrantNotExists = 11705 - - ErrCreateDomainDataSource = 11800 - ErrUpdateDomainDataSource = 11801 - ErrQueryDomainDataSource = 11802 - ErrBatchQueryDomainDataSource = 11803 - ErrDeleteDomainDataSource = 11804 - ErrDomainDataSourceExists = 11805 - ErrDomainDataSourceNotExists = 11806 - ErrDomainDataSourceInfoEncodeFailed = 11807 - ErrListDomainDataSource = 11808 -) - -func GetDomainErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func GetDomainErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainExists + return errorcode.ErrorCode_KusciaAPIErrDomainExists } return defaultErrorCode } -func GetDomainRouteErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func GetDomainRouteErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainRouteNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainRouteNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainRouteExists + return errorcode.ErrorCode_KusciaAPIErrDomainRouteExists } return defaultErrorCode } -func CreateDomainDataErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func CreateDomainDataErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataExists } return defaultErrorCode } -func GetDomainDataErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func GetDomainDataErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainDataNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataExists } return defaultErrorCode } -func CreateDomainDataGrantErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func CreateDomainDataGrantErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataGrantExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataGrantExists } return defaultErrorCode } -func GetDomainDataGrantErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func GetDomainDataGrantErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainDataGrantNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataGrantNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataGrantExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataGrantExists } return defaultErrorCode } -func CreateDomainDataSourceErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func CreateDomainDataSourceErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataSourceExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataSourceExists } return defaultErrorCode } -func GetDomainDataSourceErrorCode(err error, defaultErrorCode errorcode.KusciaErrorCode) errorcode.KusciaErrorCode { +func GetDomainDataSourceErrorCode(err error, defaultErrorCode errorcode.ErrorCode) errorcode.ErrorCode { if errors.IsNotFound(err) { - return ErrDomainDataSourceNotExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataSourceNotExists } if errors.IsAlreadyExists(err) { - return ErrDomainDataSourceExists + return errorcode.ErrorCode_KusciaAPIErrDomainDataSourceExists } return defaultErrorCode } diff --git a/pkg/kusciaapi/handler/grpchandler/certificate.go b/pkg/kusciaapi/handler/grpchandler/certificate.go index 515f4fd8..8ce2d5af 100644 --- a/pkg/kusciaapi/handler/grpchandler/certificate.go +++ b/pkg/kusciaapi/handler/grpchandler/certificate.go @@ -19,9 +19,9 @@ import ( "encoding/json" "github.com/secretflow/kuscia/pkg/confmanager/service" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" "github.com/secretflow/kuscia/pkg/web/utils" "github.com/secretflow/kuscia/proto/api/v1alpha1/confmanager" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -42,13 +42,13 @@ func (h *certificateHandler) GenerateKeyCerts(ctx context.Context, request *kusc kapiResp := &kusciaapi.GenerateKeyCertsResponse{} if err := CopyValue(request, cmReq); err != nil { return &kusciaapi.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrForUnexpected, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrForUnexpected, err.Error()), }, nil } cmResp := h.certificateService.GenerateKeyCerts(ctx, cmReq) if err := CopyValue(cmResp, kapiResp); err != nil { return &kusciaapi.GenerateKeyCertsResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrForUnexpected, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrForUnexpected, err.Error()), }, nil } return kapiResp, nil diff --git a/pkg/kusciaapi/handler/grpchandler/domain_handler.go b/pkg/kusciaapi/handler/grpchandler/domain_handler.go index 46bf35af..c63755d4 100644 --- a/pkg/kusciaapi/handler/grpchandler/domain_handler.go +++ b/pkg/kusciaapi/handler/grpchandler/domain_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package grpchandler import ( diff --git a/pkg/kusciaapi/handler/grpchandler/domaindata_grant_handler.go b/pkg/kusciaapi/handler/grpchandler/domaindata_grant_handler.go index 4c916d71..79ab41a1 100644 --- a/pkg/kusciaapi/handler/grpchandler/domaindata_grant_handler.go +++ b/pkg/kusciaapi/handler/grpchandler/domaindata_grant_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package grpchandler import ( diff --git a/pkg/kusciaapi/handler/grpchandler/domaindata_handler.go b/pkg/kusciaapi/handler/grpchandler/domaindata_handler.go index 8a3ce0d1..27ea5199 100644 --- a/pkg/kusciaapi/handler/grpchandler/domaindata_handler.go +++ b/pkg/kusciaapi/handler/grpchandler/domaindata_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package grpchandler import ( diff --git a/pkg/kusciaapi/handler/grpchandler/domaindata_source_handler.go b/pkg/kusciaapi/handler/grpchandler/domaindata_source_handler.go index 49556b9e..6566fca3 100644 --- a/pkg/kusciaapi/handler/grpchandler/domaindata_source_handler.go +++ b/pkg/kusciaapi/handler/grpchandler/domaindata_source_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package grpchandler import ( diff --git a/pkg/kusciaapi/handler/grpchandler/serving_handler.go b/pkg/kusciaapi/handler/grpchandler/serving_handler.go index ce670da9..629353d2 100644 --- a/pkg/kusciaapi/handler/grpchandler/serving_handler.go +++ b/pkg/kusciaapi/handler/grpchandler/serving_handler.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package grpchandler import ( diff --git a/pkg/kusciaapi/handler/httphandler/domaindata/create.go b/pkg/kusciaapi/handler/httphandler/domaindata/create.go index 2fdbefc4..8c141e84 100644 --- a/pkg/kusciaapi/handler/httphandler/domaindata/create.go +++ b/pkg/kusciaapi/handler/httphandler/domaindata/create.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/kusciaapi/handler/httphandler/domaindata/delete.go b/pkg/kusciaapi/handler/httphandler/domaindata/delete.go index 251198bc..3d0f5538 100644 --- a/pkg/kusciaapi/handler/httphandler/domaindata/delete.go +++ b/pkg/kusciaapi/handler/httphandler/domaindata/delete.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/kusciaapi/handler/httphandler/domaindata/query.go b/pkg/kusciaapi/handler/httphandler/domaindata/query.go index a4567462..34dd9828 100644 --- a/pkg/kusciaapi/handler/httphandler/domaindata/query.go +++ b/pkg/kusciaapi/handler/httphandler/domaindata/query.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/kusciaapi/handler/httphandler/domaindata/update.go b/pkg/kusciaapi/handler/httphandler/domaindata/update.go index ccae80fa..fae87b42 100644 --- a/pkg/kusciaapi/handler/httphandler/domaindata/update.go +++ b/pkg/kusciaapi/handler/httphandler/domaindata/update.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package domaindata import ( diff --git a/pkg/kusciaapi/service/apilite_service_test.go b/pkg/kusciaapi/service/apilite_service_test.go index bae6ab23..b717a657 100644 --- a/pkg/kusciaapi/service/apilite_service_test.go +++ b/pkg/kusciaapi/service/apilite_service_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( diff --git a/pkg/kusciaapi/service/domain_route_service.go b/pkg/kusciaapi/service/domain_route_service.go index a4cbf6ab..0dfe2d04 100644 --- a/pkg/kusciaapi/service/domain_route_service.go +++ b/pkg/kusciaapi/service/domain_route_service.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -33,6 +33,7 @@ import ( "github.com/secretflow/kuscia/pkg/utils/nlog" consts "github.com/secretflow/kuscia/pkg/web/constants" "github.com/secretflow/kuscia/pkg/web/utils" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -64,24 +65,24 @@ func (s domainRouteService) CreateDomainRoute(ctx context.Context, request *kusc // do validate if err := validateCreateDomainRouteRequest(request); err != nil { return &kusciaapi.CreateDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandlerViaDestination(ctx, request); err != nil { return &kusciaapi.CreateDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // check source domain exists - if errorCode, errMsg := CheckDomainExists(s.kusciaClient, request.GetSource()); utils.ResponseCodeSuccess != errorCode { + if errorCode, errMsg := CheckDomainExists(s.kusciaClient, request.GetSource()); pberrorcode.ErrorCode_SUCCESS != errorCode { return &kusciaapi.CreateDomainRouteResponse{ Status: utils.BuildErrorResponseStatus(errorCode, errMsg), } } // check destination domain exists - if errorCode, errMsg := CheckDomainExists(s.kusciaClient, request.GetDestination()); utils.ResponseCodeSuccess != errorCode { + if errorCode, errMsg := CheckDomainExists(s.kusciaClient, request.GetDestination()); pberrorcode.ErrorCode_SUCCESS != errorCode { return &kusciaapi.CreateDomainRouteResponse{ Status: utils.BuildErrorResponseStatus(errorCode, errMsg), } @@ -98,7 +99,7 @@ func (s domainRouteService) CreateDomainRoute(ctx context.Context, request *kusc drProtocol, isTLS, err := convert2DomainRouteProtocol(port.Protocol) if err != nil { return &kusciaapi.CreateDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, errorcode.ErrCreateDomainRoute), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainRoute), err.Error()), } } cdrEndpoint.Ports[i] = v1alpha1.DomainPort{ @@ -186,7 +187,7 @@ func (s domainRouteService) CreateDomainRoute(ctx context.Context, request *kusc _, err := s.kusciaClient.KusciaV1alpha1().ClusterDomainRoutes().Create(ctx, clusterDomainRoute, metav1.CreateOptions{}) if err != nil { return &kusciaapi.CreateDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, errorcode.ErrCreateDomainRoute), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainRoute), err.Error()), } } return &kusciaapi.CreateDomainRouteResponse{ @@ -198,13 +199,13 @@ func (s domainRouteService) DeleteDomainRoute(ctx context.Context, request *kusc // do validate if err := validateDomainRouteRequest(request); err != nil { return &kusciaapi.DeleteDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandlerViaDestination(ctx, request); err != nil { return &kusciaapi.DeleteDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // delete cluster domain kusciaAPIDomainRoute @@ -212,7 +213,7 @@ func (s domainRouteService) DeleteDomainRoute(ctx context.Context, request *kusc err := s.kusciaClient.KusciaV1alpha1().ClusterDomainRoutes().Delete(ctx, name, metav1.DeleteOptions{}) if err != nil { return &kusciaapi.DeleteDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, errorcode.ErrDeleteDomainRoute), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrDeleteDomainRoute), err.Error()), } } return &kusciaapi.DeleteDomainRouteResponse{ @@ -224,13 +225,13 @@ func (s domainRouteService) QueryDomainRoute(ctx context.Context, request *kusci // do validate if err := validateDomainRouteRequest(request); err != nil { return &kusciaapi.QueryDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandlerViaDstAndSrc(ctx, request); err != nil { return &kusciaapi.QueryDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // get cdr from k8s @@ -238,7 +239,7 @@ func (s domainRouteService) QueryDomainRoute(ctx context.Context, request *kusci cdr, err := s.kusciaClient.KusciaV1alpha1().ClusterDomainRoutes().Get(ctx, name, metav1.GetOptions{}) if err != nil { return &kusciaapi.QueryDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, errorcode.ErrQueryDomainRoute), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomainRoute), err.Error()), } } cdrSpec := cdr.Spec @@ -323,20 +324,20 @@ func (s domainRouteService) BatchQueryDomainRouteStatus(ctx context.Context, req routeKeys := request.RouteKeys if len(routeKeys) == 0 { return &kusciaapi.BatchQueryDomainRouteStatusResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "DomainRoute keys can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "DomainRoute keys can not be empty"), } } for i, key := range routeKeys { if err := validateDomainRouteRequest(key); err != nil { nlog.Errorf("Validate BatchQueryDomainRouteStatusRequest the index: %d of route key, failed: %s.", i, err.Error()) return &kusciaapi.BatchQueryDomainRouteStatusResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth handler if err := s.authHandlerViaDstAndSrc(ctx, key); err != nil { return &kusciaapi.BatchQueryDomainRouteStatusResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } } @@ -348,7 +349,7 @@ func (s domainRouteService) BatchQueryDomainRouteStatus(ctx context.Context, req cdr, err := s.kusciaClient.KusciaV1alpha1().ClusterDomainRoutes().Get(ctx, name, metav1.GetOptions{}) if err != nil { return &kusciaapi.BatchQueryDomainRouteStatusResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, errorcode.ErrQueryDomainRouteStatus), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainRouteErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomainRouteStatus), err.Error()), } } routeStatuses[i] = &kusciaapi.DomainRouteStatus{ diff --git a/pkg/kusciaapi/service/domain_route_service_lite.go b/pkg/kusciaapi/service/domain_route_service_lite.go index 053e64a4..95a4d124 100644 --- a/pkg/kusciaapi/service/domain_route_service_lite.go +++ b/pkg/kusciaapi/service/domain_route_service_lite.go @@ -17,10 +17,10 @@ package service import ( "context" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" "github.com/secretflow/kuscia/pkg/kusciaapi/proxy" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/web/utils" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -32,14 +32,14 @@ func (s domainRouteServiceLite) CreateDomainRoute(ctx context.Context, request * // do validate if err := validateCreateDomainRouteRequest(request); err != nil { return &kusciaapi.CreateDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // request the master api resp, err := s.kusciaAPIClient.CreateDomainRoute(ctx, request) if err != nil { return &kusciaapi.CreateDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -49,14 +49,14 @@ func (s domainRouteServiceLite) DeleteDomainRoute(ctx context.Context, request * // do validate if err := validateDomainRouteRequest(request); err != nil { return &kusciaapi.DeleteDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // request the master api resp, err := s.kusciaAPIClient.DeleteDomainRoute(ctx, request) if err != nil { return &kusciaapi.DeleteDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -66,14 +66,14 @@ func (s domainRouteServiceLite) QueryDomainRoute(ctx context.Context, request *k // do validate if err := validateDomainRouteRequest(request); err != nil { return &kusciaapi.QueryDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // request the master api resp, err := s.kusciaAPIClient.QueryDomainRoute(ctx, request) if err != nil { return &kusciaapi.QueryDomainRouteResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -84,14 +84,14 @@ func (s domainRouteServiceLite) BatchQueryDomainRouteStatus(ctx context.Context, routeKeys := request.RouteKeys if len(routeKeys) == 0 { return &kusciaapi.BatchQueryDomainRouteStatusResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "DomainRoute keys can not be empty"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "DomainRoute keys can not be empty"), } } for i, key := range routeKeys { if err := validateDomainRouteRequest(key); err != nil { nlog.Errorf("Validate BatchQueryDomainRouteStatusRequest the index: %d of route key, failed: %s.", i, err.Error()) return &kusciaapi.BatchQueryDomainRouteStatusResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } } @@ -99,7 +99,7 @@ func (s domainRouteServiceLite) BatchQueryDomainRouteStatus(ctx context.Context, resp, err := s.kusciaAPIClient.BatchQueryDomainRoute(ctx, request) if err != nil { return &kusciaapi.BatchQueryDomainRouteStatusResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp diff --git a/pkg/kusciaapi/service/domain_route_service_test.go b/pkg/kusciaapi/service/domain_route_service_test.go index 6267d528..77208af7 100644 --- a/pkg/kusciaapi/service/domain_route_service_test.go +++ b/pkg/kusciaapi/service/domain_route_service_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -31,7 +31,7 @@ func TestCreateDomainRouteWithAllDomainNotExists(t *testing.T) { res := createDomainRoute() t.Logf("CreateDomainRoute res : %+v\n", res) assert.NotNil(t, res) - assert.Equal(t, int32(errorcode.ErrDomainNotExists), res.Status.Code) + assert.Equal(t, int32(errorcode.ErrorCode_KusciaAPIErrDomainNotExists), res.Status.Code) } func TestCreateDomainRouteWithSourceDomainNotExists(t *testing.T) { @@ -44,7 +44,7 @@ func TestCreateDomainRouteWithSourceDomainNotExists(t *testing.T) { res := createDomainRoute() t.Log(fmt.Sprintf("CreateDomainRoute res : %+v\n", res)) assert.NotNil(t, res) - assert.Equal(t, int32(errorcode.ErrDomainNotExists), res.Status.Code) + assert.Equal(t, int32(errorcode.ErrorCode_KusciaAPIErrDomainNotExists), res.Status.Code) } func TestCreateDomainRoute(t *testing.T) { @@ -90,11 +90,11 @@ func TestCreateDomainRouteWithTransit(t *testing.T) { }{ { request: &domainRoutes[0], - code: errorcode.ErrRequestValidate, + code: int32(errorcode.ErrorCode_KusciaAPIErrRequestValidate), }, { request: &domainRoutes[1], - code: errorcode.ErrRequestValidate, + code: int32(errorcode.ErrorCode_KusciaAPIErrRequestValidate), }, { request: &domainRoutes[2], @@ -163,7 +163,7 @@ func TestDeleteRoute(t *testing.T) { Source: kusciaAPIDR.source, Destination: kusciaAPIDR.destination, }) - assert.Equal(t, queryRes.Status.Code, int32(errorcode.ErrDomainRouteNotExists)) + assert.Equal(t, queryRes.Status.Code, int32(errorcode.ErrorCode_KusciaAPIErrDomainRouteNotExists)) } func TestConvertDomainRouteProtocol(t *testing.T) { diff --git a/pkg/kusciaapi/service/domain_service.go b/pkg/kusciaapi/service/domain_service.go index 4d06a99a..36f7d44d 100644 --- a/pkg/kusciaapi/service/domain_service.go +++ b/pkg/kusciaapi/service/domain_service.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -21,6 +21,7 @@ import ( "encoding/pem" "errors" "fmt" + "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -30,15 +31,14 @@ import ( "github.com/secretflow/kuscia/pkg/kusciaapi/config" consts "github.com/secretflow/kuscia/pkg/kusciaapi/constants" "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" - "github.com/secretflow/kuscia/pkg/kusciaapi/proxy" apiutils "github.com/secretflow/kuscia/pkg/kusciaapi/utils" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/resources" "github.com/secretflow/kuscia/pkg/utils/tls" "github.com/secretflow/kuscia/pkg/web/constants" - weberrorcode "github.com/secretflow/kuscia/pkg/web/errorcode" "github.com/secretflow/kuscia/pkg/web/utils" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -75,20 +75,24 @@ func (s domainService) CreateDomain(ctx context.Context, request *kusciaapi.Crea domainID := request.DomainId if domainID == "" { return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), + } + } else if domainID == common.UnSupportedDomainID { + return &kusciaapi.CreateDomainResponse{ + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be 'master', please choose another name"), } } // do k8s validate if err := resources.ValidateK8sName(domainID, "doamin_id"); err != nil { return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } role := request.Role if role != "" && role != string(v1alpha1.Partner) { return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("role is invalid, must be empty or %s", v1alpha1.Partner)), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("role is invalid, must be empty or %s", v1alpha1.Partner)), } } // 1. role is empty, domain to create is located in local party @@ -97,21 +101,23 @@ func (s domainService) CreateDomain(ctx context.Context, request *kusciaapi.Crea domain, err := s.kusciaClient.KusciaV1alpha1().Domains().Get(ctx, request.MasterDomainId, metav1.GetOptions{}) if err != nil { return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("check master domain id failed with err %v", err.Error())), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("check master domain id failed with err %v", err.Error())), } } if domain.Spec.Role != v1alpha1.DomainRole(request.Role) { return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("master domain role is %s, but current role is %s", domain.Spec.Role, request.Role)), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("master domain role is %s, but current role is %s", domain.Spec.Role, request.Role)), } } } // do cert validate - if len(request.Cert) > 0 { - if err := tls.VerifyEncodeCert(request.Cert); err != nil { + inputCert := request.Cert + if len(inputCert) > 0 { + var err error + if inputCert, err = s.getValidCert(inputCert); err != nil { return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } } @@ -122,7 +128,7 @@ func (s domainService) CreateDomain(ctx context.Context, request *kusciaapi.Crea Name: domainID, }, Spec: v1alpha1.DomainSpec{ - Cert: request.Cert, + Cert: inputCert, Role: v1alpha1.DomainRole(request.Role), AuthCenter: authCenterConverter(request.AuthCenter), MasterDomain: request.MasterDomainId, @@ -140,7 +146,7 @@ func (s domainService) CreateDomain(ctx context.Context, request *kusciaapi.Crea _, err := s.kusciaClient.KusciaV1alpha1().Domains().Create(ctx, kusciaDomain, metav1.CreateOptions{}) if err != nil { return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, errorcode.ErrCreateDomain), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomain), err.Error()), } } return &kusciaapi.CreateDomainResponse{ @@ -153,7 +159,7 @@ func (s domainService) QueryDomain(ctx context.Context, request *kusciaapi.Query domainID := request.DomainId if domainID == "" { return &kusciaapi.QueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), } } if request.GetDomainId() == consts.KusciaMasterDomain { @@ -162,14 +168,14 @@ func (s domainService) QueryDomain(ctx context.Context, request *kusciaapi.Query // Auth Handler if err := s.authHandler(ctx, request); err != nil { return &kusciaapi.QueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // get kuscia domain kusciaDomain, err := s.kusciaClient.KusciaV1alpha1().Domains().Get(ctx, domainID, metav1.GetOptions{}) if err != nil { return &kusciaapi.QueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, errorcode.ErrQueryDomain), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomain), err.Error()), } } // build domain response @@ -202,7 +208,7 @@ func (s domainService) queryKusciaMasterDomain() *kusciaapi.QueryDomainResponse caCert, err := s.queryKusciaMasterCert() if err != nil { return &kusciaapi.QueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrQueryDomain, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrQueryDomain, err.Error()), } } return &kusciaapi.QueryDomainResponse{ @@ -216,7 +222,7 @@ func (s domainService) queryKusciaMasterDomain() *kusciaapi.QueryDomainResponse func (s domainService) queryKusciaMasterCert() (string, error) { if s.conf.RootCA == nil { - errMsg := fmt.Sprintf("master ca cert is nil") + errMsg := "master ca cert is nil" nlog.Errorf("Query kuscia-master cert failed, error: %s.", errMsg) return "", errors.New(errMsg) } @@ -229,19 +235,19 @@ func (s domainService) UpdateDomain(ctx context.Context, request *kusciaapi.Upda domainID := request.DomainId if domainID == "" { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), } } role := request.Role if role != "" && role != string(v1alpha1.Partner) { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("role is invalid, must be empty or %s", v1alpha1.Partner)), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("role is invalid, must be empty or %s", v1alpha1.Partner)), } } // Auth Handler if err := s.authHandler(ctx, request); err != nil { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // 1. role is empty, domain to create is located in local party @@ -250,21 +256,23 @@ func (s domainService) UpdateDomain(ctx context.Context, request *kusciaapi.Upda domain, err := s.kusciaClient.KusciaV1alpha1().Domains().Get(ctx, request.MasterDomainId, metav1.GetOptions{}) if err != nil { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("check master domain id failed with err %v", err.Error())), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("check master domain id failed with err %v", err.Error())), } } if domain.Spec.Role != v1alpha1.DomainRole(request.Role) { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("master domain role is %s, but current role is %s", domain.Spec.Role, request.Role)), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("master domain role is %s, but current role is %s", domain.Spec.Role, request.Role)), } } } // do cert validate + inputCert := request.Cert if len(request.Cert) > 0 { - if err := tls.VerifyEncodeCert(request.Cert); err != nil { + var err error + if inputCert, err = s.getValidCert(inputCert); err != nil { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } } @@ -273,7 +281,7 @@ func (s domainService) UpdateDomain(ctx context.Context, request *kusciaapi.Upda latestDomain, err := s.kusciaClient.KusciaV1alpha1().Domains().Get(ctx, domainID, metav1.GetOptions{}) if err != nil { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, errorcode.ErrUpdateDomain), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrUpdateDomain), err.Error()), } } // build kuscia domain @@ -283,7 +291,7 @@ func (s domainService) UpdateDomain(ctx context.Context, request *kusciaapi.Upda ResourceVersion: latestDomain.ResourceVersion, }, Spec: v1alpha1.DomainSpec{ - Cert: request.Cert, + Cert: inputCert, Role: v1alpha1.DomainRole(role), AuthCenter: authCenterConverter(request.AuthCenter), MasterDomain: request.MasterDomainId, @@ -293,7 +301,7 @@ func (s domainService) UpdateDomain(ctx context.Context, request *kusciaapi.Upda _, err = s.kusciaClient.KusciaV1alpha1().Domains().Update(ctx, kusciaDomain, metav1.UpdateOptions{}) if err != nil { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrUpdateDomain, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrUpdateDomain, err.Error()), } } return &kusciaapi.UpdateDomainResponse{ @@ -306,14 +314,14 @@ func (s domainService) DeleteDomain(ctx context.Context, request *kusciaapi.Dele domainID := request.DomainId if domainID == "" { return &kusciaapi.DeleteDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), } } // delete kuscia domain err := s.kusciaClient.KusciaV1alpha1().Domains().Delete(ctx, domainID, metav1.DeleteOptions{}) if err != nil { return &kusciaapi.DeleteDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, errorcode.ErrDeleteDomain), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrDeleteDomain), err.Error()), } } return &kusciaapi.DeleteDomainResponse{ @@ -326,13 +334,13 @@ func (s domainService) BatchQueryDomain(ctx context.Context, request *kusciaapi. domainIDs := request.DomainIds if len(domainIDs) == 0 { return &kusciaapi.BatchQueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain ids can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain ids can not be empty"), } } for i, domainID := range domainIDs { if domainID == "" { return &kusciaapi.BatchQueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("domain id can not be empty on index %d", i)), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("domain id can not be empty on index %d", i)), } } } @@ -343,7 +351,7 @@ func (s domainService) BatchQueryDomain(ctx context.Context, request *kusciaapi. caCert, err := s.queryKusciaMasterCert() if err != nil { return &kusciaapi.BatchQueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, errorcode.ErrQueryDomainStatus), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomainStatus), err.Error()), } } domains[i] = &kusciaapi.Domain{ @@ -355,7 +363,7 @@ func (s domainService) BatchQueryDomain(ctx context.Context, request *kusciaapi. kusciaDomain, err := s.kusciaClient.KusciaV1alpha1().Domains().Get(ctx, domainID, metav1.GetOptions{}) if err != nil { return &kusciaapi.BatchQueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, errorcode.ErrQueryDomainStatus), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomainStatus), err.Error()), } } domains[i], _ = s.buildDomain(kusciaDomain) @@ -368,12 +376,12 @@ func (s domainService) BatchQueryDomain(ctx context.Context, request *kusciaapi. } } -func CheckDomainExists(kusciaclient kusciaclientset.Interface, domainId string) (kusciaError weberrorcode.KusciaErrorCode, errorMsg string) { - _, err := kusciaclient.KusciaV1alpha1().Domains().Get(context.Background(), domainId, metav1.GetOptions{}) +func CheckDomainExists(kusciaClient kusciaclientset.Interface, domainId string) (kusciaError pberrorcode.ErrorCode, errorMsg string) { + _, err := kusciaClient.KusciaV1alpha1().Domains().Get(context.Background(), domainId, metav1.GetOptions{}) if err != nil { - return errorcode.GetDomainErrorCode(err, errorcode.ErrQueryDomain), err.Error() + return errorcode.GetDomainErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomain), err.Error() } - return utils.ResponseCodeSuccess, "" + return pberrorcode.ErrorCode_SUCCESS, "" } func (s domainService) buildDomain(kusciaDomain *v1alpha1.Domain) (*kusciaapi.Domain, []*kusciaapi.DeployTokenStatus) { @@ -437,3 +445,61 @@ func (s domainService) authHandler(ctx context.Context, request RequestWithDomai } return nil } + +func (s domainService) getValidCert(inputCert string) (string, error) { + var orgError error + + inputCert = strings.Trim(inputCert, "\n") + + if err := tls.VerifyEncodeCert(inputCert); err == nil { + return inputCert, nil + } else { + orgError = err + } + + // compatible with raw cert + inputCert_1 := base64.StdEncoding.EncodeToString([]byte(inputCert)) + if err := tls.VerifyEncodeCert(inputCert_1); err == nil { + return inputCert_1, nil + } + + // input cert remove begin and end + if !strings.HasPrefix(inputCert, "-----BEGIN CERTIFICATE-----") { + inputCert_2 := "-----BEGIN CERTIFICATE-----\n" + inputCert + "\n-----END CERTIFICATE-----" + inputCert_2 = base64.StdEncoding.EncodeToString([]byte(inputCert_2)) + if err := tls.VerifyEncodeCert(inputCert_2); err == nil { + return inputCert_2, nil + } + } + + // input cret removed "\n" + if !strings.Contains(inputCert, "\n") { + if len(inputCert) >= 1040 { + inputCert_3 := "-----BEGIN CERTIFICATE-----\n" + for i := 0; i < 16; i++ { + inputCert_3 = inputCert_3 + inputCert[(i*64):(i+1)*64] + "\n" + } + inputCert_3 = inputCert_3 + "-----END CERTIFICATE-----" + + inputCert_3 = base64.StdEncoding.EncodeToString([]byte(inputCert_3)) + if err := tls.VerifyEncodeCert(inputCert_3); err == nil { + return inputCert_3, nil + } + } + } + + // input cert base64 encode 2 times + if inputCert_4, err := base64.StdEncoding.DecodeString(inputCert); err == nil { + if err := tls.VerifyEncodeCert(string(inputCert_4)); err == nil { + return string(inputCert_4), nil + } + } + + // copy from window, has "\r" + if strings.Contains(inputCert, "\r") { + inputCert5 := strings.ReplaceAll(inputCert, "\r", "") + return s.getValidCert(inputCert5) + } + + return "", orgError +} diff --git a/pkg/kusciaapi/service/domain_service_lite.go b/pkg/kusciaapi/service/domain_service_lite.go index 5154108e..fc869dcf 100644 --- a/pkg/kusciaapi/service/domain_service_lite.go +++ b/pkg/kusciaapi/service/domain_service_lite.go @@ -19,9 +19,9 @@ import ( "fmt" "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" "github.com/secretflow/kuscia/pkg/kusciaapi/proxy" "github.com/secretflow/kuscia/pkg/web/utils" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -32,7 +32,7 @@ type domainServiceLite struct { func (s domainServiceLite) CreateDomain(ctx context.Context, request *kusciaapi.CreateDomainRequest) *kusciaapi.CreateDomainResponse { // kuscia lite api not support this interface return &kusciaapi.CreateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrLiteAPINotSupport, "kuscia lite api not support this interface now"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrLiteAPINotSupport, "kuscia lite api not support this interface now"), } } @@ -41,14 +41,14 @@ func (s domainServiceLite) QueryDomain(ctx context.Context, request *kusciaapi.Q domainID := request.DomainId if domainID == "" { return &kusciaapi.QueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), } } // request the master api resp, err := s.kusciaAPIClient.QueryDomain(ctx, request) if err != nil { return &kusciaapi.QueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -59,20 +59,20 @@ func (s domainServiceLite) UpdateDomain(ctx context.Context, request *kusciaapi. domainID := request.DomainId if domainID == "" { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), } } role := request.Role if role != "" && role != string(v1alpha1.Partner) { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("role is invalid, must be empty or %s", v1alpha1.Partner)), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("role is invalid, must be empty or %s", v1alpha1.Partner)), } } // request the master api resp, err := s.kusciaAPIClient.UpdateDomain(ctx, request) if err != nil { return &kusciaapi.UpdateDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } @@ -82,7 +82,7 @@ func (s domainServiceLite) UpdateDomain(ctx context.Context, request *kusciaapi. func (s domainServiceLite) DeleteDomain(ctx context.Context, request *kusciaapi.DeleteDomainRequest) *kusciaapi.DeleteDomainResponse { // kuscia lite api not support this interface return &kusciaapi.DeleteDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrLiteAPINotSupport, "kuscia lite api not support this interface now"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrLiteAPINotSupport, "kuscia lite api not support this interface now"), } } @@ -91,13 +91,13 @@ func (s domainServiceLite) BatchQueryDomain(ctx context.Context, request *kuscia domainIDs := request.DomainIds if len(domainIDs) == 0 { return &kusciaapi.BatchQueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain ids can not be empty"), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain ids can not be empty"), } } for i, domainID := range domainIDs { if domainID == "" { return &kusciaapi.BatchQueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("domain id can not be empty on index %d", i)), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("domain id can not be empty on index %d", i)), } } } @@ -105,7 +105,7 @@ func (s domainServiceLite) BatchQueryDomain(ctx context.Context, request *kuscia resp, err := s.kusciaAPIClient.BatchQueryDomain(ctx, request) if err != nil { return &kusciaapi.BatchQueryDomainResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp diff --git a/pkg/kusciaapi/service/domain_service_test.go b/pkg/kusciaapi/service/domain_service_test.go index dc0ca884..816e7f59 100644 --- a/pkg/kusciaapi/service/domain_service_test.go +++ b/pkg/kusciaapi/service/domain_service_test.go @@ -12,17 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( "context" + "encoding/base64" + "strings" "testing" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" + "github.com/stretchr/testify/assert" + + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" "github.com/secretflow/kuscia/test/util" - "github.com/stretchr/testify/assert" ) func TestCreateDomain(t *testing.T) { @@ -32,13 +35,29 @@ func TestCreateDomain(t *testing.T) { assert.NotNil(t, res) } +func TestCreateDomain_NameError(t *testing.T) { + t.Parallel() + ds := &domainService{} + res := ds.CreateDomain(context.Background(), &kusciaapi.CreateDomainRequest{ + DomainId: "", + }) + assert.NotNil(t, res) + assert.Equal(t, int32(errorcode.ErrorCode_KusciaAPIErrRequestValidate), res.Status.Code) + + res = ds.CreateDomain(context.Background(), &kusciaapi.CreateDomainRequest{ + DomainId: "master", + }) + assert.NotNil(t, res) + assert.Equal(t, int32(errorcode.ErrorCode_KusciaAPIErrRequestValidate), res.Status.Code) +} + func TestCreateDomainWithCertError(t *testing.T) { res := kusciaAPIDS.CreateDomain(context.Background(), &kusciaapi.CreateDomainRequest{ DomainId: kusciaAPIDS.domainID, Cert: "cert", }) assert.NotNil(t, res) - assert.Equal(t, res.Status.Code, int32(errorcode.ErrRequestValidate)) + assert.Equal(t, res.Status.Code, int32(errorcode.ErrorCode_KusciaAPIErrRequestValidate)) } func TestCreateDomainWithCertSuccess(t *testing.T) { @@ -71,7 +90,7 @@ func TestUpdateDomainWithCertError(t *testing.T) { Cert: "cert", }) assert.NotNil(t, res) - assert.Equal(t, res.Status.Code, int32(errorcode.ErrRequestValidate)) + assert.Equal(t, res.Status.Code, int32(errorcode.ErrorCode_KusciaAPIErrRequestValidate)) } func TestUpdateDomainWithCertSuccess(t *testing.T) { @@ -98,5 +117,45 @@ func TestDeleteDomain(t *testing.T) { queryRes := kusciaAPIDS.QueryDomain(context.Background(), &kusciaapi.QueryDomainRequest{ DomainId: kusciaAPIDS.domainID, }) - assert.Equal(t, queryRes.Status.Code, int32(errorcode.ErrDomainNotExists)) + assert.Equal(t, queryRes.Status.Code, int32(errorcode.ErrorCode_KusciaAPIErrDomainNotExists)) +} + +func TestGetValidCert(t *testing.T) { + t.Parallel() + + rawContent := util.MakeCertString(t) + + //var res string + var err error + + ds := &domainService{} + assert.NotNil(t, ds) + + _, err = ds.getValidCert(rawContent) + assert.NoError(t, err) + + // base64 1 times + input1 := base64.StdEncoding.EncodeToString([]byte(rawContent)) + _, err = ds.getValidCert(input1) + assert.NoError(t, err) + + // remove begin/end + input2 := strings.TrimSuffix(strings.TrimPrefix(rawContent, "-----BEGIN CERTIFICATE-----"), "-----END CERTIFICATE-----") + _, err = ds.getValidCert(input2) + assert.NoError(t, err) + + // remove all "\n" + input3 := strings.ReplaceAll(input2, "\n", "") + _, err = ds.getValidCert(input3) + assert.NoError(t, err) + + // base64 2 times + input4 := base64.StdEncoding.EncodeToString([]byte(input1)) + _, err = ds.getValidCert(input4) + assert.NoError(t, err) + + // has "\r" + input5 := strings.ReplaceAll(rawContent, "\n", "\r\n") + _, err = ds.getValidCert(input5) + assert.NoError(t, err) } diff --git a/pkg/kusciaapi/service/domaindata_grant.go b/pkg/kusciaapi/service/domaindata_grant.go index 50ae3a7f..38c958f6 100644 --- a/pkg/kusciaapi/service/domaindata_grant.go +++ b/pkg/kusciaapi/service/domaindata_grant.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -36,6 +36,7 @@ import ( "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/resources" "github.com/secretflow/kuscia/pkg/web/utils" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -65,21 +66,21 @@ func (s *domainDataGrantService) CreateDomainDataGrant(ctx context.Context, requ // do validate if validateErr := validateCreateDomainDataGrantRequest(request); validateErr != nil { return &kusciaapi.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, validateErr.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, validateErr.Error()), } } dd, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDatas(request.DomainId).Get(ctx, request.DomaindataId, metav1.GetOptions{}) if err != nil { return &kusciaapi.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("domaindata [%s] not exists", request.DomaindataId)), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("domaindata [%s] not exists", request.DomaindataId)), } } if request.DomaindatagrantId != "" { _, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDataGrants(request.DomainId).Get(ctx, request.DomaindatagrantId, metav1.GetOptions{}) if err == nil { return &kusciaapi.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrCreateDomainDataGrant), fmt.Sprintf("CreateDomainDataGrant failed, because domaindatagrant %s is exist", request.DomaindatagrantId)), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataGrant), fmt.Sprintf("CreateDomainDataGrant failed, because domaindatagrant %s is exist", request.DomaindatagrantId)), } } } @@ -102,7 +103,7 @@ func (s *domainDataGrantService) CreateDomainDataGrant(ctx context.Context, requ if err != nil { nlog.Errorf("CreateDomainDataGrant failed, error:%s", err.Error()) return &kusciaapi.CreateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.CreateDomainDataGrantErrorCode(err, errorcode.ErrCreateDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.CreateDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataGrant), err.Error()), } } return &kusciaapi.CreateDomainDataGrantResponse{ @@ -119,7 +120,7 @@ func (s *domainDataGrantService) QueryDomainDataGrant(ctx context.Context, reque if err != nil { nlog.Errorf("Query DomainDataGrant failed, error:%s", err.Error()) return &kusciaapi.QueryDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrQueryDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomainDataGrant), err.Error()), } } @@ -151,23 +152,23 @@ func (s *domainDataGrantService) UpdateDomainDataGrant(ctx context.Context, requ if request.DomaindataId == "" { return &kusciaapi.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domaindataid cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domaindataid cant be null"), } } dd, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDatas(request.DomainId).Get(ctx, request.DomaindataId, metav1.GetOptions{}) if err != nil { return &kusciaapi.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domaindata cant be found"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domaindata cant be found"), } } if request.GrantDomain == "" { return &kusciaapi.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "grantdomain cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "grantdomain cant be null"), } } if request.DomaindatagrantId == "" { return &kusciaapi.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "grantdomainid cant be null"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "grantdomainid cant be null"), } } @@ -175,7 +176,7 @@ func (s *domainDataGrantService) UpdateDomainDataGrant(ctx context.Context, requ if err != nil { nlog.Errorf("Get DomainDataGrant failed, error:%s", err.Error()) return &kusciaapi.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrQueryDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomainDataGrant), err.Error()), } } @@ -199,7 +200,7 @@ func (s *domainDataGrantService) UpdateDomainDataGrant(ctx context.Context, requ if err != nil { nlog.Errorf("Update DomainDataGrant failed, error:%s", err.Error()) return &kusciaapi.UpdateDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrUpdateDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrUpdateDomainDataGrant), err.Error()), } } return &kusciaapi.UpdateDomainDataGrantResponse{ @@ -213,7 +214,7 @@ func (s *domainDataGrantService) DeleteDomainDataGrant(ctx context.Context, requ if err != nil { nlog.Errorf("Delete domainDataGrantId:%s failed, detail:%s", request.DomaindatagrantId, err.Error()) return &kusciaapi.DeleteDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrDeleteDomainDataGrant), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrDeleteDomainDataGrant), err.Error()), } } return &kusciaapi.DeleteDomainDataGrantResponse{ @@ -225,7 +226,7 @@ func (s *domainDataGrantService) ListDomainDataGrant(ctx context.Context, reques // do validate if request.Data == nil || request.Data.DomainId == "" { return &kusciaapi.ListDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), } } // construct label selector @@ -265,7 +266,7 @@ func (s *domainDataGrantService) ListDomainDataGrant(ctx context.Context, reques if err != nil { nlog.Errorf("List DomainData failed, error:%s", err.Error()) return &kusciaapi.ListDomainDataGrantResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, errorcode.ErrListDomainDataFailed), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataGrantErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrListDomainDataFailed), err.Error()), } } grantLists := make([]*kusciaapi.DomainDataGrant, len(dataList.Items)) diff --git a/pkg/kusciaapi/service/domaindata_grant_test.go b/pkg/kusciaapi/service/domaindata_grant_test.go index 429e1171..eb3cb0f4 100644 --- a/pkg/kusciaapi/service/domaindata_grant_test.go +++ b/pkg/kusciaapi/service/domaindata_grant_test.go @@ -12,17 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( "context" "testing" - "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" - "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" "github.com/stretchr/testify/assert" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" + "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) func TestDomainDataGrant(t *testing.T) { diff --git a/pkg/kusciaapi/service/domaindata_service.go b/pkg/kusciaapi/service/domaindata_service.go index ab2b6d82..d14b267e 100644 --- a/pkg/kusciaapi/service/domaindata_service.go +++ b/pkg/kusciaapi/service/domaindata_service.go @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( "context" "fmt" + "path/filepath" "strings" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -27,6 +28,7 @@ import ( "github.com/secretflow/kuscia/pkg/common" "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" + "github.com/secretflow/kuscia/pkg/datamesh/service" "github.com/secretflow/kuscia/pkg/kusciaapi/config" "github.com/secretflow/kuscia/pkg/kusciaapi/constants" "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" @@ -35,6 +37,7 @@ import ( consts "github.com/secretflow/kuscia/pkg/web/constants" "github.com/secretflow/kuscia/pkg/web/utils" pbv1alpha1 "github.com/secretflow/kuscia/proto/api/v1alpha1" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -61,30 +64,30 @@ func (s domainDataService) CreateDomainData(ctx context.Context, request *kuscia // validate domainID if request.DomainId == "" { return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "request domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "request domain id can not be empty"), } } if err := resources.ValidateK8sName(request.DomainId, "domain_id"); err != nil { return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // validate data type if request.Type == "" { return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "request type can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "request type can not be empty"), } } // validate relative_uri if request.RelativeUri == "" { return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "request relative_uri can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "request relative_uri can not be empty"), } } // validate lite request if err := s.validateRequestWhenLite(request); err != nil { return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } @@ -93,7 +96,7 @@ func (s domainDataService) CreateDomainData(ctx context.Context, request *kuscia // do k8s validate if err := resources.ValidateK8sName(request.DomaindataId, "domaindata_id"); err != nil { return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } domainData, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDatas(request.DomainId).Get(ctx, request.DomaindataId, metav1.GetOptions{}) @@ -106,7 +109,7 @@ func (s domainDataService) CreateDomainData(ctx context.Context, request *kuscia // auth pre handler if err := s.authHandler(ctx, request); err != nil { return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } @@ -150,7 +153,7 @@ func (s domainDataService) CreateDomainData(ctx context.Context, request *kuscia if err != nil { nlog.Errorf("CreateDomainData failed, error: %s", err.Error()) return &kusciaapi.CreateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.CreateDomainDataErrorCode(err, errorcode.ErrCreateDomainDataFailed), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.CreateDomainDataErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataFailed), err.Error()), } } return &kusciaapi.CreateDomainDataResponse{ @@ -165,19 +168,19 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *kuscia // do validate if request.DomaindataId == "" || request.DomainId == "" { return &kusciaapi.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id and domaindata id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id and domaindata id can not be empty"), } } if err := s.validateRequestWhenLite(request); err != nil { return &kusciaapi.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandler(ctx, request); err != nil { return &kusciaapi.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // get original domainData from k8s @@ -185,7 +188,7 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *kuscia if err != nil { nlog.Errorf("UpdateDomainData failed, error: %s", err.Error()) return &kusciaapi.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, errorcode.ErrGetDomainDataFailed), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrGetDomainDataFailed), err.Error()), } } @@ -221,12 +224,12 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *kuscia }, } // merge modifiedDomainData to originalDomainData - patchBytes, originalBytes, modifiedBytes, err := common.MergeDomainData(originalDomainData, modifiedDomainData) + patchBytes, originalBytes, modifiedBytes, err := service.MergeDomainData(originalDomainData, modifiedDomainData) if err != nil { nlog.Errorf("Merge DomainData failed, request: %+v,error: %s.", request, err.Error()) return &kusciaapi.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrMergeDomainDataFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrMergeDomainDataFailed, err.Error()), } } nlog.Debugf("Update DomainData request: %+v, patchBytes: %s, originalDomainData: %s, modifiedDomainData: %s.", @@ -237,7 +240,7 @@ func (s domainDataService) UpdateDomainData(ctx context.Context, request *kuscia nlog.Debugf("Patch DomainData failed, request: %+v, patchBytes: %s, originalDomainData: %s, modifiedDomainData: %s, error: %s.", request, patchBytes, originalBytes, modifiedBytes, err.Error()) return &kusciaapi.UpdateDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, errorcode.ErrPatchDomainDataFailed), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrPatchDomainDataFailed), err.Error()), } } // construct the response @@ -250,18 +253,18 @@ func (s domainDataService) DeleteDomainData(ctx context.Context, request *kuscia // do validate if request.DomaindataId == "" || request.DomainId == "" { return &kusciaapi.DeleteDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id and domaindata id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id and domaindata id can not be empty"), } } if err := s.validateRequestWhenLite(request); err != nil { return &kusciaapi.DeleteDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandler(ctx, request); err != nil { return &kusciaapi.DeleteDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // record the delete operation @@ -271,7 +274,7 @@ func (s domainDataService) DeleteDomainData(ctx context.Context, request *kuscia if err != nil { nlog.Errorf("Delete domainData: %s failed, detail: %s", request.DomaindataId, err.Error()) return &kusciaapi.DeleteDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, errorcode.ErrDeleteDomainDataFailed), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrDeleteDomainDataFailed), err.Error()), } } return &kusciaapi.DeleteDomainDataResponse{ @@ -283,18 +286,18 @@ func (s domainDataService) QueryDomainData(ctx context.Context, request *kusciaa // do validate if request.Data == nil || request.Data.DomaindataId == "" || request.Data.DomainId == "" { return &kusciaapi.QueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id and domaindata id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id and domaindata id can not be empty"), } } if err := s.validateRequestWhenLite(request.Data); err != nil { return &kusciaapi.QueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandler(ctx, request.Data); err != nil { return &kusciaapi.QueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // get kuscia domain @@ -302,7 +305,7 @@ func (s domainDataService) QueryDomainData(ctx context.Context, request *kusciaa if err != nil { nlog.Errorf("QueryDomainData failed, error: %s", err.Error()) return &kusciaapi.QueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, errorcode.ErrGetDomainDataFailed), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrGetDomainDataFailed), err.Error()), } } // build domain response @@ -331,19 +334,19 @@ func (s domainDataService) BatchQueryDomainData(ctx context.Context, request *ku for _, v := range request.Data { if v.GetDomainId() == "" || v.GetDomaindataId() == "" { return &kusciaapi.BatchQueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id and domaindata id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id and domaindata id can not be empty"), } } // check the request when this is kuscia lite api if err := s.validateRequestWhenLite(v); err != nil { return &kusciaapi.BatchQueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandler(ctx, v); err != nil { return &kusciaapi.BatchQueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } } @@ -357,7 +360,7 @@ func (s domainDataService) BatchQueryDomainData(ctx context.Context, request *ku } nlog.Errorf("QueryDomainData failed, error: %s", err.Error()) return &kusciaapi.BatchQueryDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrGetDomainDataFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrGetDomainDataFailed, err.Error()), } } domainData := kusciaapi.DomainData{ @@ -390,18 +393,18 @@ func (s domainDataService) ListDomainData(ctx context.Context, request *kusciaap // do validate if request.Data == nil || request.Data.DomainId == "" { return &kusciaapi.ListDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain id can not be empty"), } } if err := s.validateRequestWhenLite(request.Data); err != nil { return &kusciaapi.ListDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth pre handler if err := s.authHandler(ctx, request.Data); err != nil { return &kusciaapi.ListDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // construct label selector @@ -435,7 +438,7 @@ func (s domainDataService) ListDomainData(ctx context.Context, request *kusciaap if err != nil { nlog.Errorf("List DomainData failed, error: %s", err.Error()) return &kusciaapi.ListDomainDataResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrListDomainDataFailed, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrListDomainDataFailed, err.Error()), } } respDatas := make([]*kusciaapi.DomainData, len(dataList.Items)) @@ -513,6 +516,12 @@ func (s domainDataService) normalizationCreateRequest(request *kusciaapi.CreateD if request.Vendor == "" { request.Vendor = common.DefaultDomainDataVendor } + // truncate slash + // fix Issue: + // datasource-path: "/home/admin" ; datasource-prefix: "/test/" ; domainData-relativeURI: "/data/alice.csv" + // os.path.join(path,prefix,uri) would be /data/alice.csv , this is not expect, expect is /home/admin/test/data/alice.csv + // so trim the prefix filepath.Separator + request.RelativeUri = strings.TrimPrefix(request.RelativeUri, string(filepath.Separator)) } func (s domainDataService) normalizationUpdateRequest(request *kusciaapi.UpdateDomainDataRequest, data v1alpha1.DomainDataSpec) { diff --git a/pkg/kusciaapi/service/domaindata_service_test.go b/pkg/kusciaapi/service/domaindata_service_test.go index 4d388346..88f5d9e4 100644 --- a/pkg/kusciaapi/service/domaindata_service_test.go +++ b/pkg/kusciaapi/service/domaindata_service_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( diff --git a/pkg/kusciaapi/service/domaindata_source.go b/pkg/kusciaapi/service/domaindata_source.go index c604d930..d459fabe 100644 --- a/pkg/kusciaapi/service/domaindata_source.go +++ b/pkg/kusciaapi/service/domaindata_source.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "path/filepath" "strings" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -35,6 +36,7 @@ import ( "github.com/secretflow/kuscia/pkg/utils/tls" "github.com/secretflow/kuscia/pkg/web/utils" "github.com/secretflow/kuscia/proto/api/v1alpha1/confmanager" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -77,14 +79,14 @@ func (s domainDataSourceService) CreateDomainDataSource(ctx context.Context, req if err = s.validateRequestIdentity(request.DomainId); err != nil { nlog.Errorf(errCreateDomainDataSource, err.Error()) return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if err = validateDataSourceType(request.Type); err != nil { nlog.Errorf(errCreateDomainDataSource, err.Error()) return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } @@ -94,13 +96,13 @@ func (s domainDataSourceService) CreateDomainDataSource(ctx context.Context, req if err = resources.ValidateK8sName(request.DatasourceId, "datasource_id"); err != nil { return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if (request.InfoKey == nil || *request.InfoKey == "") && request.Info == nil { return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrCreateDomainDataSource), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataSource), fmt.Sprintf("domain data source info key and info all empty")), } } @@ -108,7 +110,7 @@ func (s domainDataSourceService) CreateDomainDataSource(ctx context.Context, req domainDataSource, err := s.conf.KusciaClient.KusciaV1alpha1().DomainDataSources(request.DomainId).Get(ctx, request.DatasourceId, metav1.GetOptions{}) if domainDataSource != nil && err == nil { return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrCreateDomainDataSource), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataSource), fmt.Sprintf("domain data source %s already exist", request.DatasourceId)), } } @@ -131,14 +133,14 @@ func (s domainDataSourceService) CreateDomainDataSource(ctx context.Context, req datasourceInfo, err := s.getDsInfoByKey(ctx, request.Type, *request.InfoKey) if err != nil { return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrCreateDomainDataSource), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataSource), fmt.Sprintf("domain data source info key %s not exist", *request.InfoKey)), } } - uri, err := parseDataSourceURI(request.Type, datasourceInfo) + uri, err := parseAndNormalizeDataSource(request.Type, datasourceInfo) if err != nil { return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrCreateDomainDataSource), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataSource), fmt.Sprintf("domain data source info key %s can not convert to datasource info", *request.InfoKey)), } } @@ -149,7 +151,7 @@ func (s domainDataSourceService) CreateDomainDataSource(ctx context.Context, req if err != nil { nlog.Errorf(errCreateDomainDataSource, err.Error()) return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrCreateDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataSource), err.Error()), } } dataSource.Spec.URI = uri @@ -165,7 +167,7 @@ func (s domainDataSourceService) CreateDomainDataSource(ctx context.Context, req if err != nil { nlog.Errorf(errCreateDomainDataSource, err.Error()) return &kusciaapi.CreateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.CreateDomainDataSourceErrorCode(err, errorcode.ErrCreateDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.CreateDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrCreateDomainDataSource), err.Error()), } } @@ -182,13 +184,13 @@ func (s domainDataSourceService) UpdateDomainDataSource(ctx context.Context, req if err = s.validateRequestIdentity(request.DomainId); err != nil { nlog.Errorf(errUpdateDomainDataSource, err.Error()) return &kusciaapi.UpdateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if request.DatasourceId == "" { return &kusciaapi.UpdateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain data source id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain data source id can not be empty"), } } @@ -196,13 +198,13 @@ func (s domainDataSourceService) UpdateDomainDataSource(ctx context.Context, req if err != nil { if k8serrors.IsNotFound(err) { return &kusciaapi.UpdateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrUpdateDomainDataSource), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrUpdateDomainDataSource), fmt.Sprintf("domain %v data source %v doesn't exist", request.DomainId, request.DatasourceId)), } } nlog.Errorf(errUpdateDomainDataSource, err.Error()) return &kusciaapi.UpdateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrUpdateDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrUpdateDomainDataSource), err.Error()), } } @@ -212,7 +214,7 @@ func (s domainDataSourceService) UpdateDomainDataSource(ctx context.Context, req if err != nil { nlog.Errorf(errUpdateDomainDataSource, err.Error()) return &kusciaapi.UpdateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrUpdateDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrUpdateDomainDataSource), err.Error()), } } @@ -222,7 +224,7 @@ func (s domainDataSourceService) UpdateDomainDataSource(ctx context.Context, req if err != nil { nlog.Errorf(errUpdateDomainDataSource, err.Error()) return &kusciaapi.UpdateDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrUpdateDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrUpdateDomainDataSource), err.Error()), } } } @@ -287,13 +289,13 @@ func (s domainDataSourceService) DeleteDomainDataSource(ctx context.Context, req if err = s.validateRequestIdentity(request.DomainId); err != nil { nlog.Errorf(errDeleteDomainDataSource, err.Error()) return &kusciaapi.DeleteDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if request.DatasourceId == "" { return &kusciaapi.DeleteDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "domain data source id can not be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "domain data source id can not be empty"), } } @@ -306,7 +308,7 @@ func (s domainDataSourceService) DeleteDomainDataSource(ctx context.Context, req } nlog.Errorf(errDeleteDomainDataSource, err.Error()) return &kusciaapi.DeleteDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrDeleteDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrDeleteDomainDataSource), err.Error()), } } @@ -320,7 +322,7 @@ func (s domainDataSourceService) QueryDomainDataSource(ctx context.Context, requ if err != nil { nlog.Errorf(errQueryDomainDataSource, err.Error()) return &kusciaapi.QueryDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrQueryDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrQueryDomainDataSource), err.Error()), } } @@ -333,7 +335,7 @@ func (s domainDataSourceService) QueryDomainDataSource(ctx context.Context, requ func (s domainDataSourceService) BatchQueryDomainDataSource(ctx context.Context, request *kusciaapi.BatchQueryDomainDataSourceRequest) *kusciaapi.BatchQueryDomainDataSourceResponse { if request.Data == nil { return &kusciaapi.BatchQueryDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "request data can't be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "request data can't be empty"), } } @@ -343,7 +345,7 @@ func (s domainDataSourceService) BatchQueryDomainDataSource(ctx context.Context, if err != nil { nlog.Errorf(errBatchQueryDomainDataSource, err.Error()) return &kusciaapi.BatchQueryDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, errorcode.ErrBatchQueryDomainDataSource), err.Error()), + Status: utils.BuildErrorResponseStatus(errorcode.GetDomainDataSourceErrorCode(err, pberrorcode.ErrorCode_KusciaAPIErrBatchQueryDomainDataSource), err.Error()), } } data = append(data, dataSource) @@ -358,13 +360,13 @@ func (s domainDataSourceService) BatchQueryDomainDataSource(ctx context.Context, func (s domainDataSourceService) ListDomainDataSource(ctx context.Context, request *kusciaapi.ListDomainDataSourceRequest) *kusciaapi.ListDomainDataSourceResponse { if request.DomainId == "" { return &kusciaapi.ListDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "request domainID can't be empty"), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "request domainID can't be empty"), } } if err := s.validateRetrieveRequest(request.DomainId); err != nil { nlog.Errorf(errListDomainDataSource, err.Error()) return &kusciaapi.ListDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrListDomainDataSource, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrListDomainDataSource, err.Error()), } } var data []*kusciaapi.DomainDataSource @@ -400,7 +402,7 @@ func (s domainDataSourceService) ListDomainDataSource(ctx context.Context, reque returnErr: nlog.Error(err.Error()) return &kusciaapi.ListDomainDataSourceResponse{ - Status: utils.BuildErrorResponseStatus(errorcode.ErrListDomainDataSource, err.Error()), + Status: utils.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrListDomainDataSource, err.Error()), } } @@ -498,12 +500,12 @@ func (s domainDataSourceService) validateRetrieveRequest(domainID string) error return nil } -//nolint:dupl +// nolint:dulp func (s domainDataSourceService) getDsInfoByKey(ctx context.Context, sourceType string, infoKey string) (*kusciaapi.DataSourceInfo, error) { response := s.configurationService.QueryConfiguration(ctx, &confmanager.QueryConfigurationRequest{ Ids: []string{infoKey}, }, s.conf.DomainID) - if response.Status.Code != utils.ResponseCodeSuccess { + if !utils.IsSuccessCode(response.Status.Code) { nlog.Errorf("Query info key failed, code: %d, message: %s", response.Status.Code, response.Status.Message) return nil, fmt.Errorf("query info key failed") } @@ -523,9 +525,9 @@ func (s domainDataSourceService) getDsInfoByKey(ctx context.Context, sourceType return info, err } -//nolint:dupl +// nolint:dulp func (s domainDataSourceService) encryptInfo(dataSourceType string, info *kusciaapi.DataSourceInfo) (uri string, encInfo string, err error) { - uri, err = parseDataSourceURI(dataSourceType, info) + uri, err = parseAndNormalizeDataSource(dataSourceType, info) if err != nil { return "", "", fmt.Errorf("parse data source failed, %v", err) } @@ -545,7 +547,7 @@ func (s domainDataSourceService) encryptInfo(dataSourceType string, info *kuscia return } -//nolint:dupl +// nolint:dulp func (s domainDataSourceService) decryptInfo(cipherInfo string) (*kusciaapi.DataSourceInfo, error) { plaintext, err := tls.DecryptOAEP(s.conf.DomainKey, cipherInfo) if err != nil { @@ -583,8 +585,8 @@ func decodeDataSourceInfo(sourceType string, connectionStr string) (*kusciaapi.D return &dsInfo, nil } -//nolint:dupl -func parseDataSourceURI(sourceType string, info *kusciaapi.DataSourceInfo) (uri string, err error) { +// nolint:dulp +func parseAndNormalizeDataSource(sourceType string, info *kusciaapi.DataSourceInfo) (uri string, err error) { if info == nil { return "", errors.New("info is nil") } @@ -602,11 +604,20 @@ func parseDataSourceURI(sourceType string, info *kusciaapi.DataSourceInfo) (uri if isInvalid(info.Oss == nil) { return } - uri = strings.TrimRight(info.Oss.Bucket, "/") + "/" + strings.TrimLeft(info.Oss.Prefix, "/") + // truncate slash + // fix Issue: + // datasource-path: "/home/admin" ; datasource-prefix: "/test/" ; domainData-relativeURI: "/data/alice.csv" + // os.path.join(path,prefix,uri) would be /data/alice.csv , this is not expect, expect is /home/admin/test/data/alice.csv + // so trim the prefix filepath.Separator + info.Oss.Bucket = strings.Trim(info.Oss.Bucket, string(filepath.Separator)) + info.Oss.Prefix = strings.Trim(info.Oss.Prefix, string(filepath.Separator)) + uri = filepath.Join(info.Oss.Bucket, info.Oss.Prefix) case common.DomainDataSourceTypeLocalFS: if isInvalid(info.Localfs == nil) { return } + // truncate slash + info.Localfs.Path = strings.TrimRight(info.Localfs.Path, string(filepath.Separator)) uri = info.Localfs.Path case common.DomainDataSourceTypeMysql: if isInvalid(info.Database == nil) { diff --git a/pkg/kusciaapi/service/domaindata_source_test.go b/pkg/kusciaapi/service/domaindata_source_test.go index ee5bcadf..9df3e970 100644 --- a/pkg/kusciaapi/service/domaindata_source_test.go +++ b/pkg/kusciaapi/service/domaindata_source_test.go @@ -28,9 +28,10 @@ import ( "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciafake "github.com/secretflow/kuscia/pkg/crd/clientset/versioned/fake" "github.com/secretflow/kuscia/pkg/kusciaapi/config" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" "github.com/secretflow/kuscia/pkg/secretbackend" _ "github.com/secretflow/kuscia/pkg/secretbackend/mem" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -86,7 +87,7 @@ func TestCreateDomainDataSource_InfoKeyNotExists(t *testing.T) { InfoKey: &makeInfoKey, }) - assert.EqualValues(t, res.Status.Code, errorcode.ErrCreateDomainDataSource) + assert.EqualValues(t, res.Status.Code, errorcode.ErrorCode_KusciaAPIErrCreateDomainDataSource) } func TestUpdateDomainDataSource(t *testing.T) { @@ -244,7 +245,7 @@ func TestListDomainDataSource_DomainNotExist(t *testing.T) { res := dsService.ListDomainDataSource(context.Background(), &kusciaapi.ListDomainDataSourceRequest{ DomainId: "mock-domain-id", }) - assert.Equal(t, int32(errorcode.ErrListDomainDataSource), res.Status.Code) + assert.Equal(t, int32(errorcode.ErrorCode_KusciaAPIErrListDomainDataSource), res.Status.Code) } func TestListDomainDataSource_InfoErr(t *testing.T) { @@ -265,7 +266,7 @@ func TestListDomainDataSource_InfoErr(t *testing.T) { res := dsService.ListDomainDataSource(context.Background(), &kusciaapi.ListDomainDataSourceRequest{ DomainId: mockDomainId, }) - assert.Equal(t, int32(errorcode.ErrListDomainDataSource), res.Status.Code) + assert.Equal(t, int32(pberrorcode.ErrorCode_KusciaAPIErrListDomainDataSource), res.Status.Code) } func makeDomainDataSourceService(t *testing.T, conf *config.KusciaAPIConfig) IDomainDataSourceService { diff --git a/pkg/kusciaapi/service/health_service.go b/pkg/kusciaapi/service/health_service.go index 7305ebdb..652a91a9 100644 --- a/pkg/kusciaapi/service/health_service.go +++ b/pkg/kusciaapi/service/health_service.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( diff --git a/pkg/kusciaapi/service/job_service.go b/pkg/kusciaapi/service/job_service.go index f373c7ec..cf8bf92f 100644 --- a/pkg/kusciaapi/service/job_service.go +++ b/pkg/kusciaapi/service/job_service.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -35,13 +35,13 @@ import ( "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciaclientset "github.com/secretflow/kuscia/pkg/crd/clientset/versioned" "github.com/secretflow/kuscia/pkg/kusciaapi/config" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" "github.com/secretflow/kuscia/pkg/kusciaapi/proxy" "github.com/secretflow/kuscia/pkg/kusciaapi/utils" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/resources" consts "github.com/secretflow/kuscia/pkg/web/constants" utils2 "github.com/secretflow/kuscia/pkg/web/utils" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -82,13 +82,13 @@ func (h *jobService) CreateJob(ctx context.Context, request *kusciaapi.CreateJob // do validate if err := validateCreateJobRequest(request, h.Initiator); err != nil { return &kusciaapi.CreateJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // auth handler if err := h.authHandlerJobCreate(ctx, request); err != nil { return &kusciaapi.CreateJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // convert createJobRequest to kuscia job @@ -107,7 +107,7 @@ func (h *jobService) CreateJob(ctx context.Context, request *kusciaapi.CreateJob limitResource[corev1.ResourceCPU] = q } else { return &kusciaapi.CreateJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("parse input cpu resource failed: %v", err.Error())), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("parse input cpu resource failed: %v", err.Error())), } } } @@ -116,7 +116,7 @@ func (h *jobService) CreateJob(ctx context.Context, request *kusciaapi.CreateJob limitResource[corev1.ResourceMemory] = q } else { return &kusciaapi.CreateJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("parse input memory resource failed: %v", err.Error())), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("parse input memory resource failed: %v", err.Error())), } } } @@ -168,7 +168,7 @@ func (h *jobService) CreateJob(ctx context.Context, request *kusciaapi.CreateJob _, err := h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Create(ctx, kusciaJob, metav1.CreateOptions{}) if err != nil { return &kusciaapi.CreateJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrCreateJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrCreateJob, err.Error()), } } return &kusciaapi.CreateJobResponse{ @@ -184,14 +184,14 @@ func (h *jobService) QueryJob(ctx context.Context, request *kusciaapi.QueryJobRe jobID := request.JobId if jobID == "" { return &kusciaapi.QueryJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // build job status kusciaJob, jobStatus, err := h.buildJobStatusByID(ctx, jobID) if err != nil { return &kusciaapi.QueryJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrQueryJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrQueryJob, err.Error()), } } // custom fields @@ -250,20 +250,20 @@ func (h *jobService) DeleteJob(ctx context.Context, request *kusciaapi.DeleteJob jobID := request.JobId if jobID == "" { return &kusciaapi.DeleteJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // auth handler if err := h.authHandlerJobDelete(ctx, jobID); err != nil { return &kusciaapi.DeleteJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } // delete kuscia job err := h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Delete(ctx, jobID, metav1.DeleteOptions{}) if err != nil { return &kusciaapi.DeleteJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrDeleteJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrDeleteJob, err.Error()), } } return &kusciaapi.DeleteJobResponse{ @@ -279,28 +279,28 @@ func (h *jobService) StopJob(ctx context.Context, request *kusciaapi.StopJobRequ jobID := request.JobId if jobID == "" { return &kusciaapi.StopJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // get domain from context _, domainId := GetRoleAndDomainFromCtx(ctx) if len(domainId) == 0 { return &kusciaapi.StopJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "source domain header must be set"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "source domain header must be set"), } } job, err := h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Get(ctx, jobID, metav1.GetOptions{}) if err != nil { return &kusciaapi.StopJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrStopJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrStopJob, err.Error()), } } // auth pre handler if err = h.authHandlerJobRetrieve(ctx, job); err != nil { return &kusciaapi.StopJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } @@ -310,7 +310,7 @@ func (h *jobService) StopJob(ctx context.Context, request *kusciaapi.StopJobRequ _, err = h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Update(ctx, job, metav1.UpdateOptions{}) if err != nil { return &kusciaapi.StopJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrStopJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrStopJob, err.Error()), } } } @@ -327,14 +327,14 @@ func (h *jobService) ApproveJob(ctx context.Context, request *kusciaapi.ApproveJ jobID := request.JobId if jobID == "" { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } if request.Result != kusciaapi.ApproveResult_APPROVE_RESULT_ACCEPT && request.Result != kusciaapi.ApproveResult_APPROVE_RESULT_REJECT { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "request result must be legal"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "request result must be legal"), } } @@ -343,19 +343,19 @@ func (h *jobService) ApproveJob(ctx context.Context, request *kusciaapi.ApproveJ var domainIds []string if len(domainId) == 0 { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "source domain header must be set"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "source domain header must be set"), } } job, err := h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Get(ctx, jobID, metav1.GetOptions{}) if err != nil { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrApproveJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrApproveJob, err.Error()), } } // auth handler if err := h.authHandlerJob(ctx, job); err != nil { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } nlog.Infof("approve job %s result %v reason %s", jobID, request.Result, request.Reason) @@ -382,7 +382,7 @@ func (h *jobService) ApproveJob(ctx context.Context, request *kusciaapi.ApproveJ _, err = h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).UpdateStatus(ctx, job, metav1.UpdateOptions{}) if err != nil { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrApproveJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrApproveJob, err.Error()), } } return &kusciaapi.ApproveJobResponse{ @@ -398,31 +398,31 @@ func (h *jobService) SuspendJob(ctx context.Context, request *kusciaapi.SuspendJ jobID := request.JobId if jobID == "" { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // get domain from context _, domainId := GetRoleAndDomainFromCtx(ctx) if len(domainId) == 0 { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "source domain header must be set"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "source domain header must be set"), } } job, err := h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Get(ctx, jobID, metav1.GetOptions{}) if err != nil { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrSuspendJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrSuspendJob, err.Error()), } } // auth handler if err := h.authHandlerJob(ctx, job); err != nil { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } if job.Status.Phase != v1alpha1.KusciaJobRunning { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrSuspendNotRunningJob, fmt.Sprintf("job: %s current status is %s can not be suspend.", job.Name, job.Status.Phase)), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrSuspendNotRunningJob, fmt.Sprintf("job: %s current status is %s can not be suspend.", job.Name, job.Status.Phase)), } } nlog.Infof("Suspend job: %s, reason: %s", jobID, request.Reason) @@ -432,7 +432,7 @@ func (h *jobService) SuspendJob(ctx context.Context, request *kusciaapi.SuspendJ _, err = h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Update(ctx, job, metav1.UpdateOptions{}) if err != nil { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrSuspendJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrSuspendJob, err.Error()), } } return &kusciaapi.SuspendJobResponse{ @@ -448,31 +448,31 @@ func (h *jobService) RestartJob(ctx context.Context, request *kusciaapi.RestartJ jobID := request.JobId if jobID == "" { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // get domain from context _, domainId := GetRoleAndDomainFromCtx(ctx) if len(domainId) == 0 { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "source domain header must be set"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "source domain header must be set"), } } job, err := h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Get(ctx, jobID, metav1.GetOptions{}) if err != nil { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRestartJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRestartJob, err.Error()), } } // auth handler if err := h.authHandlerJob(ctx, job); err != nil { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } if job.Status.Phase != v1alpha1.KusciaJobFailed && job.Status.Phase != v1alpha1.KusciaJobSuspended { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRestartNotSuspendedOrFailedJob, fmt.Sprintf("job: %s current status is %s can not be restart.", job.Name, job.Status.Phase)), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRestartNotSuspendedOrFailedJob, fmt.Sprintf("job: %s current status is %s can not be restart.", job.Name, job.Status.Phase)), } } @@ -484,7 +484,7 @@ func (h *jobService) RestartJob(ctx context.Context, request *kusciaapi.RestartJ _, err = h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Update(ctx, job, metav1.UpdateOptions{}) if err != nil { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrSuspendJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrSuspendJob, err.Error()), } } } @@ -501,26 +501,26 @@ func (h *jobService) CancelJob(ctx context.Context, request *kusciaapi.CancelJob jobID := request.JobId if jobID == "" { return &kusciaapi.CancelJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // get domain from context _, domainId := GetRoleAndDomainFromCtx(ctx) if len(domainId) == 0 { return &kusciaapi.CancelJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "source domain header must be set"), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, "source domain header must be set"), } } job, err := h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Get(ctx, jobID, metav1.GetOptions{}) if err != nil { return &kusciaapi.CancelJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrCancelJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrCancelJob, err.Error()), } } // auth handler if err := h.authHandlerJob(ctx, job); err != nil { return &kusciaapi.CancelJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrAuthFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrAuthFailed, err.Error()), } } nlog.Infof("Cancel job: %s, reason: %s", jobID, request.Reason) @@ -529,7 +529,7 @@ func (h *jobService) CancelJob(ctx context.Context, request *kusciaapi.CancelJob _, err = h.kusciaClient.KusciaV1alpha1().KusciaJobs(common.KusciaCrossDomain).Update(ctx, job, metav1.UpdateOptions{}) if err != nil { return &kusciaapi.CancelJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrCancelJob, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrCancelJob, err.Error()), } } return &kusciaapi.CancelJobResponse{ @@ -565,7 +565,7 @@ func (h *jobService) BatchQueryJobStatus(ctx context.Context, request *kusciaapi jobIDs := request.JobIds if err := validateBatchQueryJobStatusRequest(request); err != nil { return &kusciaapi.BatchQueryJobStatusResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // build job status @@ -574,7 +574,7 @@ func (h *jobService) BatchQueryJobStatus(ctx context.Context, request *kusciaapi _, jobStatusDetail, err := h.buildJobStatusByID(ctx, jobID) if err != nil { return &kusciaapi.BatchQueryJobStatusResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrQueryJobStatus, err.Error()), + Status: utils2.BuildErrorResponseStatus(pberrorcode.ErrorCode_KusciaAPIErrQueryJobStatus, err.Error()), } } jobStatuses[i] = &kusciaapi.JobStatus{ @@ -790,20 +790,18 @@ func (h *jobService) buildJobStatus(ctx context.Context, kusciaJob *v1alpha1.Kus func (h *jobService) authHandlerJobCreate(ctx context.Context, request *kusciaapi.CreateJobRequest) error { role, domainId := GetRoleAndDomainFromCtx(ctx) - // todo: would allow if the executive node is tee + if domainId == request.Initiator { + return nil + } if role == consts.AuthRoleDomain { for _, task := range request.Tasks { - withDomain := false for _, p := range task.Parties { if p.GetDomainId() == domainId { - withDomain = true - break + return nil } } - if !withDomain { - return fmt.Errorf("domain's KusciaAPI could only create the job that the domain as a participant in the job") - } } + return fmt.Errorf("domain's KusciaAPI could only create the job that the domain as a participant in the job") } return nil } @@ -815,6 +813,9 @@ func (h *jobService) authHandlerJobDelete(ctx context.Context, jobId string) err if err != nil { return err } + if domainId == kusciaJob.Spec.Initiator { + return nil + } for _, task := range kusciaJob.Spec.Tasks { for _, p := range task.Parties { if p.DomainID == domainId { @@ -829,6 +830,9 @@ func (h *jobService) authHandlerJobDelete(ctx context.Context, jobId string) err func (h *jobService) authHandlerJobRetrieve(ctx context.Context, kusciaJob *v1alpha1.KusciaJob) error { role, domainId := GetRoleAndDomainFromCtx(ctx) + if domainId == kusciaJob.Spec.Initiator { + return nil + } if role == consts.AuthRoleDomain { for _, task := range kusciaJob.Spec.Tasks { for _, p := range task.Parties { @@ -844,6 +848,9 @@ func (h *jobService) authHandlerJobRetrieve(ctx context.Context, kusciaJob *v1al func (h *jobService) authHandlerJobWatch(ctx context.Context, kusciaJob *v1alpha1.KusciaJob) bool { role, domainId := GetRoleAndDomainFromCtx(ctx) + if domainId == kusciaJob.Spec.Initiator { + return true + } if role == consts.AuthRoleDomain { for _, task := range kusciaJob.Spec.Tasks { for _, p := range task.Parties { @@ -859,6 +866,9 @@ func (h *jobService) authHandlerJobWatch(ctx context.Context, kusciaJob *v1alpha func (h *jobService) authHandlerJob(ctx context.Context, kusciaJob *v1alpha1.KusciaJob) error { role, domainId := GetRoleAndDomainFromCtx(ctx) + if domainId == kusciaJob.Spec.Initiator { + return nil + } if role == consts.AuthRoleDomain { for _, task := range kusciaJob.Spec.Tasks { for _, p := range task.Parties { @@ -882,25 +892,21 @@ func validateCreateJobRequest(request *kusciaapi.CreateJobRequest, domainID stri if err := resources.ValidateK8sName(request.JobId, "job_id"); err != nil { return err } - // check initiator - initiator := request.Initiator - if initiator == "" { - return fmt.Errorf("initiator can not be empty") + // tasks can not be empty + if len(request.Tasks) == 0 { + return fmt.Errorf("tasks can not be empty") } - if domainID != "" && domainID != initiator { - return fmt.Errorf("request.initiator is %s, but initiator must be %s in P2P", initiator, domainID) + // check initiator + if err := validateInitiator(domainID, request.Initiator, request.Tasks); err != nil { + return err } // check maxParallelism maxParallelism := request.MaxParallelism if maxParallelism <= 0 { request.MaxParallelism = 1 } - // tasks can not be empty - tasks := request.Tasks - if len(tasks) == 0 { - return fmt.Errorf("tasks can not be empty") - } // taskId, parties can not be empty + tasks := request.Tasks for i, task := range tasks { if task.Alias == "" { return fmt.Errorf("task alias can not be empty on tasks[%d]", i) @@ -918,6 +924,16 @@ func validateCreateJobRequest(request *kusciaapi.CreateJobRequest, domainID stri return nil } +func validateInitiator(domainID, initiator string, tasks []*kusciaapi.Task) error { + if initiator == "" { + return fmt.Errorf("initiator can not be empty") + } + if domainID != "" && domainID != initiator { + return fmt.Errorf("initiator is %s, but initiator must be %s in P2P", initiator, domainID) + } + return nil +} + func validateBatchQueryJobStatusRequest(request *kusciaapi.BatchQueryJobStatusRequest) error { if len(request.JobIds) == 0 { return fmt.Errorf("job ids can not be empty") diff --git a/pkg/kusciaapi/service/job_service_lite.go b/pkg/kusciaapi/service/job_service_lite.go index e048c09b..989f7ee3 100644 --- a/pkg/kusciaapi/service/job_service_lite.go +++ b/pkg/kusciaapi/service/job_service_lite.go @@ -18,9 +18,9 @@ import ( "context" "fmt" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" "github.com/secretflow/kuscia/pkg/kusciaapi/proxy" utils2 "github.com/secretflow/kuscia/pkg/web/utils" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -33,14 +33,14 @@ func (h *jobServiceLite) CreateJob(ctx context.Context, request *kusciaapi.Creat // do validate if err := validateCreateJobRequest(request, h.Initiator); err != nil { return &kusciaapi.CreateJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // request the master api resp, err := h.kusciaAPIClient.CreateJob(ctx, request) if err != nil { return &kusciaapi.CreateJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -51,14 +51,14 @@ func (h *jobServiceLite) QueryJob(ctx context.Context, request *kusciaapi.QueryJ jobID := request.JobId if jobID == "" { return &kusciaapi.QueryJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // request the master api resp, err := h.kusciaAPIClient.QueryJob(ctx, request) if err != nil { return &kusciaapi.QueryJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -69,14 +69,14 @@ func (h *jobServiceLite) DeleteJob(ctx context.Context, request *kusciaapi.Delet jobID := request.JobId if jobID == "" { return &kusciaapi.DeleteJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // request the master api resp, err := h.kusciaAPIClient.DeleteJob(ctx, request) if err != nil { return &kusciaapi.DeleteJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -87,14 +87,14 @@ func (h *jobServiceLite) StopJob(ctx context.Context, request *kusciaapi.StopJob jobID := request.JobId if jobID == "" { return &kusciaapi.StopJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // request the master api resp, err := h.kusciaAPIClient.StopJob(ctx, request) if err != nil { return &kusciaapi.StopJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -105,14 +105,14 @@ func (h *jobServiceLite) SuspendJob(ctx context.Context, request *kusciaapi.Susp jobID := request.JobId if jobID == "" { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // request the master api resp, err := h.kusciaAPIClient.SuspendJob(ctx, request) if err != nil { return &kusciaapi.SuspendJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -123,14 +123,14 @@ func (h *jobServiceLite) RestartJob(ctx context.Context, request *kusciaapi.Rest jobID := request.JobId if jobID == "" { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // request the master api resp, err := h.kusciaAPIClient.RestartJob(ctx, request) if err != nil { return &kusciaapi.RestartJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -141,14 +141,14 @@ func (h *jobServiceLite) CancelJob(ctx context.Context, request *kusciaapi.Cance jobID := request.JobId if jobID == "" { return &kusciaapi.CancelJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } // request the master api resp, err := h.kusciaAPIClient.CancelJob(ctx, request) if err != nil { return &kusciaapi.CancelJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -159,19 +159,19 @@ func (h *jobServiceLite) ApproveJob(ctx context.Context, request *kusciaapi.Appr jobID := request.JobId if jobID == "" { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "job id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "job id can not be empty"), } } if request.Result == kusciaapi.ApproveResult_APPROVE_RESULT_UNKNOWN { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "approve result must be set"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "approve result must be set"), } } // request the master api resp, err := h.kusciaAPIClient.ApproveJob(ctx, request) if err != nil { return &kusciaapi.ApproveJobResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp @@ -181,14 +181,14 @@ func (h *jobServiceLite) BatchQueryJobStatus(ctx context.Context, request *kusci // do validate if err := validateBatchQueryJobStatusRequest(request); err != nil { return &kusciaapi.BatchQueryJobStatusResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } // request the master api resp, err := h.kusciaAPIClient.BatchQueryJob(ctx, request) if err != nil { return &kusciaapi.BatchQueryJobStatusResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestMasterFailed, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestMasterFailed, err.Error()), } } return resp diff --git a/pkg/kusciaapi/service/job_service_test.go b/pkg/kusciaapi/service/job_service_test.go index 10108125..111dbb67 100644 --- a/pkg/kusciaapi/service/job_service_test.go +++ b/pkg/kusciaapi/service/job_service_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package service import ( @@ -24,8 +24,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" consts "github.com/secretflow/kuscia/pkg/web/constants" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -133,5 +133,5 @@ func TestDeleteJob(t *testing.T) { queryRes := kusciaAPIJS.QueryJob(context.Background(), &kusciaapi.QueryJobRequest{ JobId: kusciaAPIJS.jobID, }) - assert.Equal(t, queryRes.Status.Code, int32(errorcode.ErrQueryJob)) + assert.Equal(t, queryRes.Status.Code, int32(errorcode.ErrorCode_KusciaAPIErrQueryJob)) } diff --git a/pkg/kusciaapi/service/serving_service.go b/pkg/kusciaapi/service/serving_service.go index 0da5b81f..2c1454f4 100644 --- a/pkg/kusciaapi/service/serving_service.go +++ b/pkg/kusciaapi/service/serving_service.go @@ -32,11 +32,11 @@ import ( "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" kusciaclientset "github.com/secretflow/kuscia/pkg/crd/clientset/versioned" "github.com/secretflow/kuscia/pkg/kusciaapi/config" - "github.com/secretflow/kuscia/pkg/kusciaapi/errorcode" "github.com/secretflow/kuscia/pkg/kusciaapi/utils" "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/utils/resources" utils2 "github.com/secretflow/kuscia/pkg/web/utils" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" ) @@ -70,38 +70,38 @@ func NewServingService(config *config.KusciaAPIConfig) IServingService { func (s *servingService) CreateServing(ctx context.Context, request *kusciaapi.CreateServingRequest) *kusciaapi.CreateServingResponse { if request.ServingId == "" { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "serving id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "serving id can not be empty"), } } // do k8s validate if err := resources.ValidateK8sName(request.ServingId, "serving_id"); err != nil { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if request.ServingInputConfig == "" { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "serving input config can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "serving input config can not be empty"), } } if request.Initiator == "" { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "initiator can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "initiator can not be empty"), } } if s.Initiator != "" && s.Initiator != request.Initiator { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("initiator must be %s in P2P", request.Initiator)), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("initiator must be %s in P2P", request.Initiator)), } } if len(request.Parties) == 0 { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "parties can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "parties can not be empty"), } } @@ -109,12 +109,12 @@ func (s *servingService) CreateServing(ctx context.Context, request *kusciaapi.C for i, party := range request.Parties { if party.AppImage == "" { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("appimage can not be empty in parties[%d]", i)), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("appimage can not be empty in parties[%d]", i)), } } if party.DomainId == "" { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, fmt.Sprintf("domain id can not be empty in parties[%d]", i)), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, fmt.Sprintf("domain id can not be empty in parties[%d]", i)), } } if party.DomainId == request.Initiator { @@ -123,7 +123,7 @@ func (s *servingService) CreateServing(ctx context.Context, request *kusciaapi.C } if !foundInitiator { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "initiator should be one of the parties"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "initiator should be one of the parties"), } } @@ -139,7 +139,7 @@ func (s *servingService) CreateServing(ctx context.Context, request *kusciaapi.C strategy, err := s.buildKusciaDeploymentPartyStrategy(request.Parties[i]) if err != nil { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if strategy != nil { @@ -149,7 +149,7 @@ func (s *servingService) CreateServing(ctx context.Context, request *kusciaapi.C containers, err := s.buildKusciaDeploymentPartyContainers(ctx, request.Parties[i]) if err != nil { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if len(containers) > 0 { @@ -173,7 +173,7 @@ func (s *servingService) CreateServing(ctx context.Context, request *kusciaapi.C if _, err := s.kusciaClient.KusciaV1alpha1().KusciaDeployments(common.KusciaCrossDomain).Create(ctx, kd, metav1.CreateOptions{}); err != nil { return &kusciaapi.CreateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrCreateServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrCreateServing, err.Error()), } } @@ -532,14 +532,14 @@ func (s *servingService) QueryServing(ctx context.Context, request *kusciaapi.Qu servingID := request.ServingId if servingID == "" { return &kusciaapi.QueryServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "serving id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "serving id can not be empty"), } } kd, err := s.kusciaClient.KusciaV1alpha1().KusciaDeployments(common.KusciaCrossDomain).Get(ctx, servingID, metav1.GetOptions{}) if err != nil { return &kusciaapi.QueryServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrQueryServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrQueryServing, err.Error()), } } @@ -548,14 +548,14 @@ func (s *servingService) QueryServing(ctx context.Context, request *kusciaapi.Qu partyTemplate, err := s.getAppImageTemplate(ctx, party.AppImageRef, party.Role) if err != nil { return &kusciaapi.QueryServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrQueryServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrQueryServing, err.Error()), } } resources, err := s.buildServingResources(ctx, kd, &kd.Spec.Parties[i], partyTemplate) if err != nil { return &kusciaapi.QueryServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrQueryServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrQueryServing, err.Error()), } } servingParties[i] = &kusciaapi.ServingParty{ @@ -571,7 +571,7 @@ func (s *servingService) QueryServing(ctx context.Context, request *kusciaapi.Qu status, err := s.buildServingStatusDetail(ctx, kd) if err != nil { return &kusciaapi.QueryServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrQueryServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrQueryServing, err.Error()), } } @@ -590,14 +590,14 @@ func (s *servingService) BatchQueryServingStatus(ctx context.Context, request *k servingIDs := request.ServingIds if len(servingIDs) == 0 { return &kusciaapi.BatchQueryServingStatusResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "serving ids can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "serving ids can not be empty"), } } for _, servingID := range servingIDs { if servingID == "" { return &kusciaapi.BatchQueryServingStatusResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "serving id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "serving id can not be empty"), } } } @@ -607,7 +607,7 @@ func (s *servingService) BatchQueryServingStatus(ctx context.Context, request *k servingStatusDetail, err := s.buildServingStatusByID(ctx, servingID) if err != nil { return &kusciaapi.BatchQueryServingStatusResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrQueryServingStatus, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrQueryServingStatus, err.Error()), } } servingStatuses[i] = &kusciaapi.ServingStatus{ @@ -642,7 +642,7 @@ func (s *servingService) buildServingStatusByID(ctx context.Context, servingID s func (s *servingService) UpdateServing(ctx context.Context, request *kusciaapi.UpdateServingRequest) *kusciaapi.UpdateServingResponse { if request.ServingId == "" { return &kusciaapi.UpdateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "serving id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "serving id can not be empty"), } } @@ -655,7 +655,7 @@ func (s *servingService) UpdateServing(ctx context.Context, request *kusciaapi.U kd, err := s.kusciaClient.KusciaV1alpha1().KusciaDeployments(common.KusciaCrossDomain).Get(ctx, request.ServingId, metav1.GetOptions{}) if err != nil { return &kusciaapi.UpdateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrUpdateServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrUpdateServing, err.Error()), } } @@ -672,7 +672,7 @@ func (s *servingService) UpdateServing(ctx context.Context, request *kusciaapi.U updated, err := s.updateKusciaDeploymentParty(ctx, request.ServingId, kdCopy, request.Parties[i]) if err != nil { return &kusciaapi.UpdateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, err.Error()), } } if updated { @@ -684,7 +684,7 @@ func (s *servingService) UpdateServing(ctx context.Context, request *kusciaapi.U _, err = s.kusciaClient.KusciaV1alpha1().KusciaDeployments(common.KusciaCrossDomain).Update(ctx, kdCopy, metav1.UpdateOptions{}) if err != nil { return &kusciaapi.UpdateServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrUpdateServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrUpdateServing, err.Error()), } } } @@ -872,14 +872,14 @@ func (s *servingService) DeleteServing(ctx context.Context, request *kusciaapi.D servingID := request.ServingId if servingID == "" { return &kusciaapi.DeleteServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrRequestValidate, "serving id can not be empty"), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrRequestValidate, "serving id can not be empty"), } } err := s.kusciaClient.KusciaV1alpha1().KusciaDeployments(common.KusciaCrossDomain).Delete(ctx, servingID, metav1.DeleteOptions{}) if err != nil { return &kusciaapi.DeleteServingResponse{ - Status: utils2.BuildErrorResponseStatus(errorcode.ErrDeleteServing, err.Error()), + Status: utils2.BuildErrorResponseStatus(errorcode.ErrorCode_KusciaAPIErrDeleteServing, err.Error()), } } return &kusciaapi.DeleteServingResponse{ diff --git a/pkg/transport/msq/config.go b/pkg/transport/msq/config.go index 7f57b927..b25de4cc 100644 --- a/pkg/transport/msq/config.go +++ b/pkg/transport/msq/config.go @@ -48,12 +48,6 @@ type Config struct { CleanIntervalSeconds int64 `yaml:"cleanIntervalSeconds,omitempty"` } -var config *Config - -func Init(conf *Config) { - config = conf -} - func DefaultMsgConfig() *Config { return &Config{ TotalByteSizeLimit: 1024 * 1024 * 1024 * 16, diff --git a/pkg/transport/msq/mem_control.go b/pkg/transport/msq/mem_control.go index c14a8306..6bb39bfd 100644 --- a/pkg/transport/msq/mem_control.go +++ b/pkg/transport/msq/mem_control.go @@ -18,8 +18,9 @@ import ( "sync" "time" - "github.com/secretflow/kuscia/pkg/utils/nlog" "gitlab.com/jonas.jasas/condchan" + + "github.com/secretflow/kuscia/pkg/utils/nlog" ) type MemControl struct { @@ -44,6 +45,7 @@ func NewMemControl(config *Config) *MemControl { func (mc *MemControl) Prefetch(byteSize uint64, timeout time.Duration) (bool, time.Duration) { leftTimeout := timeout mc.Lock() + defer mc.Unlock() available := mc.availableToPush(byteSize) if !available { if byteSize > mc.totalByteSizeLimit { @@ -68,7 +70,7 @@ func (mc *MemControl) Prefetch(byteSize uint64, timeout time.Duration) (bool, ti } if available = mc.availableToPush(byteSize); available { - usedTime := time.Now().Sub(start) + usedTime := time.Since(start) if leftTimeout > usedTime { leftTimeout -= usedTime } else { @@ -79,7 +81,6 @@ func (mc *MemControl) Prefetch(byteSize uint64, timeout time.Duration) (bool, ti } } - defer mc.Unlock() if !available || leftTimeout == 0 { return false, leftTimeout } diff --git a/pkg/transport/msq/session_manager.go b/pkg/transport/msq/session_manager.go index 55d1497f..e17b58da 100644 --- a/pkg/transport/msq/session_manager.go +++ b/pkg/transport/msq/session_manager.go @@ -22,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "github.com/secretflow/kuscia/pkg/transport/transerr" + "github.com/secretflow/kuscia/pkg/utils/nlog" ) type Session struct { @@ -32,16 +33,17 @@ type Session struct { type SessionManager struct { sync.RWMutex - sessions map[string]*Session - deadSessionIDs *DeadSessionID - activeSessionIDs SessionIDPQ - normalizeTimeScale time.Duration - memControl *MemControl + config *Config + sessions map[string]*Session + deadSessionIDs *DeadSessionID + activeSessionIDs SessionIDPQ + memControl *MemControl } -func NewSessionManager() *SessionManager { +func NewSessionManager(config *Config) *SessionManager { return &SessionManager{ RWMutex: sync.RWMutex{}, + config: config, sessions: make(map[string]*Session), deadSessionIDs: NewDeadSessionID(config), memControl: NewMemControl(config), @@ -58,7 +60,8 @@ func (s *SessionManager) StartCleanLoop(stopCh <-chan struct{}) { } round++ } - cleanInterval := time.Duration(config.CleanIntervalSeconds>>1) * time.Second + + cleanInterval := time.Duration((s.config.CleanIntervalSeconds*1000)>>1) * time.Millisecond go wait.Until(cleanFn, cleanInterval, stopCh) } @@ -70,6 +73,7 @@ func (s *SessionManager) Push(sid, topic string, message *Message, timeout time. ok, leftTime := s.memControl.Prefetch(message.ByteSize(), timeout) if !ok { + nlog.Warnf("All session queue total buffer(len=%d) size can't fit message(len=%d)", s.memControl.totalByteSizeLimit, message.ByteSize()) return transerr.NewTransError(transerr.BufferOverflow) } @@ -159,7 +163,7 @@ func (s *SessionManager) GetOrCreateSession(sid string, refresh bool) (*SessionQ func (s *SessionManager) cleanInactiveSession() { currentTimestamp := s.normalizedNowTimestamp() - expireDuration := config.SessionExpireSeconds / config.NormalizeActiveSeconds + expireDuration := s.config.SessionExpireSeconds / s.config.NormalizeActiveSeconds inactiveQueues := make([]*SessionQueue, 0) s.Lock() @@ -216,7 +220,7 @@ func (s *SessionManager) createSessionQueue(sid string) (*SessionQueue, *transer return nil, transerr.NewTransError(transerr.SessionReleased) } - sessionQueue := NewSessionQueue() + sessionQueue := NewSessionQueue(s.config) SessionIDItem := NewSessionIDItem(sid, s.normalizedNowTimestamp()) session := &Session{ Queue: sessionQueue, @@ -242,5 +246,5 @@ func (s *SessionManager) deleteSessionQueue(sid string) { } func (s *SessionManager) normalizedNowTimestamp() int64 { - return time.Now().Unix() / config.NormalizeActiveSeconds + return time.Now().Unix() / s.config.NormalizeActiveSeconds } diff --git a/pkg/transport/msq/session_manager_test.go b/pkg/transport/msq/session_manager_test.go index f83444a0..ff114811 100644 --- a/pkg/transport/msq/session_manager_test.go +++ b/pkg/transport/msq/session_manager_test.go @@ -20,10 +20,11 @@ import ( "time" "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/wait" ) func NewTestSessionManager() *SessionManager { - config = &Config{ + config := &Config{ TotalByteSizeLimit: 1024, PerSessionByteSizeLimit: 512, TopicQueueCapacity: 5, @@ -32,10 +33,11 @@ func NewTestSessionManager() *SessionManager { CleanIntervalSeconds: 2, NormalizeActiveSeconds: 1, } - return NewSessionManager() + return NewSessionManager(config) } func TestSessionManagerNormalPushAndPush(t *testing.T) { + t.Parallel() sm := NewTestSessionManager() // session queue full @@ -72,6 +74,7 @@ func TestSessionManagerNormalPushAndPush(t *testing.T) { } func TestSessionManagerMemControl(t *testing.T) { + t.Parallel() sm := NewTestSessionManager() sm.Push("session1", "topic", NewMessageByRandomStr(500), time.Millisecond*100) @@ -87,82 +90,140 @@ func TestSessionManagerMemControl(t *testing.T) { }() err = sm.Push("session1", "topic", NewMessageByRandomStr(12), time.Second) - processTime := time.Now().Sub(start) + processTime := time.Since(start) assert.True(t, processTime >= time.Millisecond*500) assert.Nil(t, err) } func TestWaitTwice(t *testing.T) { + t.Parallel() sm := NewTestSessionManager() - sm.Push("session1", "topic", NewMessageByRandomStr(512), time.Millisecond*100) - sm.Push("session2", "topic", NewMessageByRandomStr(500), time.Millisecond*100) - sm.Push("session3", "topic", NewMessageByRandomStr(12), time.Millisecond*100) + assert.Nil(t, sm.Push("session1", "topic", NewMessageByRandomStr(512), time.Millisecond*100)) + assert.Nil(t, sm.Push("session2", "topic", NewMessageByRandomStr(500), time.Millisecond*100)) + assert.Nil(t, sm.Push("session3", "topic", NewMessageByRandomStr(12), time.Millisecond*100)) - start := time.Now() - go func() { - time.Sleep(time.Millisecond * 300) - sm.Peek("session2", "topic") - time.Sleep(time.Millisecond * 200) - sm.Peek("session1", "topic") - }() + // total buffer is full, so return error + err := sm.Push("session1", "topic", NewMessageByRandomStr(12), time.Millisecond*20) + assert.NotNil(t, err) - err := sm.Push("session1", "topic", NewMessageByRandomStr(12), time.Millisecond*600) - processTime := time.Now().Sub(start) - assert.True(t, processTime >= time.Millisecond*500) + // session1's buffer is full, so return error + sm.Peek("session3", "topic") + err = sm.Push("session1", "topic", NewMessageByRandomStr(12), time.Millisecond*20) + assert.NotNil(t, err) + + // clean session1 + sm.Peek("session1", "topic") + start := time.Now() + err = sm.Push("session1", "topic", NewMessageByRandomStr(12), time.Millisecond*100) assert.Nil(t, err) + + processTime := time.Since(start) + assert.Less(t, processTime, time.Millisecond*10) +} + +func getQuickExpireConfig() *Config { + return &Config{ + TotalByteSizeLimit: 1024, + PerSessionByteSizeLimit: 512, + TopicQueueCapacity: 5, + DeadSessionIDExpireSeconds: 1, + SessionExpireSeconds: 1, + CleanIntervalSeconds: 1, + NormalizeActiveSeconds: 1, + } + +} + +func TestSessionExpired_Inactivate(t *testing.T) { + t.Parallel() + sm := NewSessionManager(getQuickExpireConfig()) + + assert.Nil(t, sm.Push("session1", "topic", NewMessageByRandomStr(500), time.Millisecond*100)) + assert.Equal(t, 0, len(sm.deadSessionIDs.sids)) + + assert.NoError(t, wait.Poll(time.Millisecond*100, time.Second*2, func() (bool, error) { + sm.cleanInactiveSession() + return len(sm.deadSessionIDs.sids) != 0, nil + })) + + assert.Contains(t, sm.deadSessionIDs.sids, "session1") + assert.NotContains(t, sm.sessions, "session1") } func TestSessionExpired(t *testing.T) { - sm := NewTestSessionManager() + t.Parallel() + sm := NewSessionManager(getQuickExpireConfig()) stopCh := make(chan struct{}) sm.StartCleanLoop(stopCh) sm.Push("session1", "topic", NewMessageByRandomStr(500), time.Millisecond*100) - time.Sleep(time.Second * 5) + assert.NoError(t, wait.Poll(time.Millisecond*100, time.Second*3, func() (bool, error) { + sm.Lock() + defer sm.Unlock() + _, ok := sm.sessions["session1"] + return !ok, nil + })) + + // session1 is dead msg, err := sm.Peek("session1", "topic") assert.NotNil(t, err) assert.Nil(t, msg) - err = sm.Push("session1", "topic", NewMessageByRandomStr(5), time.Millisecond*100) - assert.NotNil(t, err) - sm.Push("session2", "topic", NewMessageByRandomStr(500), time.Millisecond*100) - go func() { - for true { - select { - case <-stopCh: - break - default: - { - time.Sleep(time.Second) - _, err := sm.Peek("session2", "topic") - assert.Nil(t, err) - } - } - } - }() - time.Sleep(time.Second * 5) - _, err = sm.Pop("session2", "topic", time.Millisecond*100) - assert.Nil(t, err) + // session is dead, but not cleaned, so push return error + assert.NotNil(t, sm.Push("session1", "topic", NewMessageByRandomStr(5), time.Millisecond*100)) + + close(stopCh) +} + +func TestSessionExpired_KeepAlive(t *testing.T) { + t.Parallel() + config := getQuickExpireConfig() + config.SessionExpireSeconds = 2 + sm := NewSessionManager(config) + stopCh := make(chan struct{}) + sm.StartCleanLoop(stopCh) + + assert.Nil(t, sm.Push("session2", "topic", NewMessageByRandomStr(500), time.Millisecond*100)) + go wait.Until(func() { + _, err := sm.Peek("session2", "topic") + assert.Nil(t, err) + }, time.Millisecond*200, stopCh) + + for i := 0; i < 4; i++ { + _, ok := sm.sessions["session2"] + assert.True(t, ok) // session still exists + time.Sleep(time.Millisecond * 500) + } + close(stopCh) } func TestSessionDeadSessionID(t *testing.T) { - sm := NewTestSessionManager() - sm.Push("session1", "topic", NewMessageByRandomStr(500), time.Millisecond*100) + t.Parallel() + sm := NewSessionManager(getQuickExpireConfig()) + assert.Nil(t, sm.Push("session1", "topic", NewMessageByRandomStr(500), time.Millisecond*100)) sm.ReleaseSession("session1") - err := sm.Push("session1", "topic", NewMessageByRandomStr(5), time.Millisecond*100) - assert.NotNil(t, err) + assert.NotNil(t, sm.Push("session1", "topic", NewMessageByRandomStr(5), time.Millisecond*100)) stopCh := make(chan struct{}) sm.StartCleanLoop(stopCh) - time.Sleep(time.Second * 8) - err = sm.Push("session1", "topic", NewMessageByRandomStr(5), time.Millisecond*100) - assert.Nil(t, err) + + assert.NoError(t, wait.Poll(time.Millisecond*100, time.Second*5, func() (bool, error) { + sm.Lock() + defer sm.Unlock() + _, sExists := sm.sessions["session1"] + _, iExists := sm.deadSessionIDs.sids["session1"] + assert.False(t, sExists && iExists) // session can't both in sessions and deadsessions + return !(sExists || iExists), nil + })) + + // sesssion is dead, so can push message again + assert.Nil(t, sm.Push("session1", "topic", NewMessageByRandomStr(5), time.Millisecond*100)) } func TestRefreshSessionActiveMark(t *testing.T) { - config.NormalizeActiveSeconds = 2 + t.Parallel() sm := NewTestSessionManager() sm.Push("session1", "topic", NewMessageByRandomStr(10), time.Millisecond*100) time.Sleep(time.Second) @@ -180,6 +241,7 @@ func TestRefreshSessionActiveMark(t *testing.T) { } func TestReleaseTopicAndSession(t *testing.T) { + t.Parallel() sm := NewTestSessionManager() sm.Push("session1", "topic1", NewMessageByRandomStr(10), time.Millisecond*100) sm.Push("session1", "topic2", NewMessageByRandomStr(10), time.Millisecond*100) @@ -203,6 +265,7 @@ func TestReleaseTopicAndSession(t *testing.T) { } func TestSessionReleasedDuringWait(t *testing.T) { + t.Parallel() sm := NewTestSessionManager() sm.Push("session1", "topic1", NewMessageByRandomStr(400), time.Millisecond*100) @@ -213,7 +276,7 @@ func TestSessionReleasedDuringWait(t *testing.T) { start := time.Now() err := sm.Push("session1", "topic1", NewMessageByRandomStr(200), time.Second*2) - processTime := time.Now().Sub(start) + processTime := time.Since(start) assert.True(t, processTime > time.Second && processTime < time.Second*2) assert.NotNil(t, err) @@ -229,7 +292,7 @@ func TestSessionReleasedDuringWait(t *testing.T) { start = time.Now() _, err = sm.Pop("session2", "topic1", time.Second*2) - processTime = time.Now().Sub(start) + processTime = time.Since(start) assert.True(t, processTime > time.Second && processTime < time.Second*2) assert.NotNil(t, err) } diff --git a/pkg/transport/msq/session_queue.go b/pkg/transport/msq/session_queue.go index c5213dca..30d0c194 100644 --- a/pkg/transport/msq/session_queue.go +++ b/pkg/transport/msq/session_queue.go @@ -25,8 +25,9 @@ import ( ) type SessionQueue struct { - ByteSizeLimit uint64 - ByteSize uint64 + ByteSizeLimit uint64 + ByteSize uint64 + topicQueueCapacity int released bool @@ -37,13 +38,14 @@ type SessionQueue struct { topics map[string]*Topic } -func NewSessionQueue() *SessionQueue { +func NewSessionQueue(config *Config) *SessionQueue { sq := &SessionQueue{ - ByteSizeLimit: config.PerSessionByteSizeLimit, - ByteSize: 0, - released: false, - mtx: sync.Mutex{}, - topics: make(map[string]*Topic), + ByteSizeLimit: config.PerSessionByteSizeLimit, + ByteSize: 0, + topicQueueCapacity: config.TopicQueueCapacity, + released: false, + mtx: sync.Mutex{}, + topics: make(map[string]*Topic), } sq.notEmpty = condchan.New(&sq.mtx) @@ -128,7 +130,7 @@ func (s *SessionQueue) getTopic(topic string) *Topic { return topicQueue } - topicQueue = NewTopicQueue(topic) + topicQueue = NewTopicQueue(topic, s.topicQueueCapacity) s.topics[topic] = topicQueue return topicQueue } @@ -166,12 +168,16 @@ func (s *SessionQueue) waitUntil(check func() bool, cond *condchan.CondChan, tim return false, transerr.NewTransError(transerr.SessionReleased) } + if !available && waitTimeout { + nlog.Warnf("Message wait buffer timeout after [%s]", timeout.String()) + } + return available, nil } func (s *SessionQueue) tryPush(topic string, message *Message, timeout time.Duration) *transerr.TransError { if message.ByteSize() > s.ByteSizeLimit { - nlog.Warnf("session queue topic(%s) new message len(%d) max than total buffer size(%d)", + nlog.Warnf("Session queue topic(%s) new message len(%d) max than total buffer size(%d)", topic, message.ByteSize(), s.ByteSizeLimit) return transerr.NewTransError(transerr.BufferOverflow) } @@ -186,7 +192,7 @@ func (s *SessionQueue) tryPush(topic string, message *Message, timeout time.Dura return err } if !available { - nlog.Infof("not found available buffer for topic(%s), len(%d)", topic, message.ByteSize()) + nlog.Infof("Not found available buffer for topic(%s), len(%d)", topic, message.ByteSize()) return transerr.NewTransError(transerr.BufferOverflow) } s.innerPush(topic, message) diff --git a/pkg/transport/msq/session_queue_test.go b/pkg/transport/msq/session_queue_test.go index 97ff90ef..a732b9a9 100644 --- a/pkg/transport/msq/session_queue_test.go +++ b/pkg/transport/msq/session_queue_test.go @@ -22,8 +22,8 @@ import ( "github.com/stretchr/testify/assert" ) -func initConfig() { - config = &Config{ +func NewTestSessionQueue() *SessionQueue { + config := &Config{ TotalByteSizeLimit: 1024 * 1024, PerSessionByteSizeLimit: 1024, TopicQueueCapacity: 5, @@ -32,11 +32,7 @@ func initConfig() { CleanIntervalSeconds: 2, NormalizeActiveSeconds: 1, } -} - -func NewTestSessionQueue() *SessionQueue { - initConfig() - sq := NewSessionQueue() + sq := NewSessionQueue(config) return sq } @@ -50,7 +46,7 @@ func TestSessionQueuePushNoWait(t *testing.T) { assert.Nil(t, sq.Push("topic", NewMessageByStr("12"), time.Second)) } processTime := time.Since(start) - assert.Less(t, processTime, time.Millisecond*500) + assert.Less(t, processTime, time.Millisecond*5500) } func TestSessionQueuePushTimeout(t *testing.T) { @@ -202,14 +198,18 @@ func TestSessionQueueMultiTopic(t *testing.T) { msg, err := sq.Peek("topic1") assert.Equal(t, "hello", string(msg.Content)) assert.Nil(t, err) - _, err = sq.Peek("topic2") + msg, err = sq.Peek("topic2") + assert.Nil(t, msg) assert.Nil(t, err) err = sq.Push("topic2", NewMessageByStr("world"), time.Millisecond*100) assert.Nil(t, err) + assert.Nil(t, err) msg, err = sq.Peek("topic2") assert.Equal(t, "world", string(msg.Content)) assert.Nil(t, err) + assert.Equal(t, "world", string(msg.Content)) + assert.Nil(t, err) err = sq.Push("topic1", NewMessageByStr("hello"), time.Millisecond*100) assert.Nil(t, err) diff --git a/pkg/transport/msq/topic_queue.go b/pkg/transport/msq/topic_queue.go index 05a731f2..b99d66f1 100644 --- a/pkg/transport/msq/topic_queue.go +++ b/pkg/transport/msq/topic_queue.go @@ -29,10 +29,10 @@ func NewMessage(msg []byte) *Message { } } -func NewTopicQueue(topic string) *Topic { +func NewTopicQueue(topic string, topicQueueCapacity int) *Topic { return &Topic{ ByteSize: 0, - queue: make([]*Message, 0, config.TopicQueueCapacity), + queue: make([]*Message, 0, topicQueueCapacity), } } diff --git a/pkg/transport/msq/topic_queue_test.go b/pkg/transport/msq/topic_queue_test.go index 9ece19bf..9d7856b3 100644 --- a/pkg/transport/msq/topic_queue_test.go +++ b/pkg/transport/msq/topic_queue_test.go @@ -39,7 +39,7 @@ func NewMessageByRandomStr(l int) *Message { } func TestTopicQueue(t *testing.T) { - queue := NewTopicQueue("topic1") + queue := NewTopicQueue("topic1", 5) msg1 := NewMessageByStr("hi") msg2 := NewMessageByStr("world") queue.Push(msg1) diff --git a/pkg/transport/server/grpc/server_test.go b/pkg/transport/server/grpc/server_test.go index 5625ae58..475a4114 100644 --- a/pkg/transport/server/grpc/server_test.go +++ b/pkg/transport/server/grpc/server_test.go @@ -125,9 +125,7 @@ func TestMain(m *testing.M) { grpcConfig = config.DefaultGrpcConfig() msqConfig = msq.DefaultMsgConfig() - msq.Init(msqConfig) - - server = NewServer(grpcConfig, msq.NewSessionManager()) + server = NewServer(grpcConfig, msq.NewSessionManager(msqConfig)) go server.Start(context.Background()) time.Sleep(time.Second * 2) os.Exit(m.Run()) @@ -169,7 +167,7 @@ func TestPeek(t *testing.T) { } func TestPopWithData(t *testing.T) { - server.sm = msq.NewSessionManager() + server.sm = msq.NewSessionManager(msq.DefaultMsgConfig()) go func() { time.Sleep(time.Second * 1) err := server.sm.Push("session3", "node0-topic2", &msq.Message{Content: NewRandomStr(10)}, time.Second) @@ -189,7 +187,7 @@ func TestPopWithData(t *testing.T) { outbound := verifyPopResponse(t, popCtx, popInbound, transerr.Success) assert.Equal(t, len(outbound.Payload), 10) - processTime := time.Now().Sub(start) + processTime := time.Since(start) assert.True(t, processTime > time.Second) } @@ -208,7 +206,7 @@ func TestPopTimeout(t *testing.T) { outbound := verifyPopResponse(t, popCtx, popInbound, transerr.Success) assert.True(t, outbound.Payload == nil) - processTime := time.Now().Sub(start) + processTime := time.Since(start) assert.True(t, processTime >= time.Second*2 && processTime <= time.Second*3) } @@ -276,8 +274,8 @@ func TestPushWait(t *testing.T) { start := time.Now() pushCtx := metadata.NewOutgoingContext(context.Background(), invokeMd) verifyInvokeResponse(t, pushCtx, &pb.Inbound{Payload: NewStr("123456789")}, transerr.Success) - processTime := time.Now().Sub(start) - assert.True(t, processTime >= time.Second && processTime <= time.Second*2) + processTime := time.Since(start) + assert.True(t, processTime >= time.Second) } func TestBadRequestParam(t *testing.T) { @@ -291,8 +289,6 @@ func TestBadRequestParam(t *testing.T) { var sessionCount int = 10 var topicCount int = 5 -var stop bool = false - func producer(t *testing.T, pushSucceedCount, pushFailCount *int64) { msgLength := 256 * 1024 r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -359,7 +355,7 @@ func consumer(t *testing.T, popSucceedCount, popFailCount *int64) { client := pb.NewPrivateTransferTransportClient(dial) var outbound *pb.TransportOutbound - outbound, err = client.Pop(popCtx, popInbound) + outbound, _ = client.Pop(popCtx, popInbound) //assert.NoError(t, err) @@ -404,7 +400,7 @@ func TestPerformance(t *testing.T) { sid := fmt.Sprintf("sessionx-%d", i) for j := 0; j <= topicCount; j++ { topic := fmt.Sprintf("node0-topic-%d", j) - for true { + for { msg, _ := server.sm.Peek(sid, topic) if msg == nil { break @@ -453,9 +449,8 @@ func TestLoadOverrideGrpcTransConfig(t *testing.T) { newGrpcConfig := newGrpcTransConfig.GrpcConfig newMsqConfig := newGrpcTransConfig.MsqConfig - msq.Init(newMsqConfig) - newServer := NewServer(newGrpcConfig, msq.NewSessionManager()) + newServer := NewServer(newGrpcConfig, msq.NewSessionManager(newMsqConfig)) go newServer.Start(context.Background()) for i := 0; i < 10; i++ { diff --git a/pkg/transport/server/http/server.go b/pkg/transport/server/http/server.go index 467cb90c..cb72c684 100644 --- a/pkg/transport/server/http/server.go +++ b/pkg/transport/server/http/server.go @@ -96,8 +96,7 @@ func Run(ctx context.Context, configFile string) error { return err } - msq.Init(transConfig.MsqConfig) - sessionManager := msq.NewSessionManager() + sessionManager := msq.NewSessionManager(transConfig.MsqConfig) server := NewServer(transConfig.HTTPConfig, sessionManager) return server.Start(ctx) } diff --git a/pkg/transport/server/http/server_test.go b/pkg/transport/server/http/server_test.go index 163d060f..f4fb2933 100644 --- a/pkg/transport/server/http/server_test.go +++ b/pkg/transport/server/http/server_test.go @@ -86,8 +86,7 @@ func TestMain(m *testing.M) { httpConfig.Port = 2001 msqConfig = msq.DefaultMsgConfig() - msq.Init(msqConfig) - server = NewServer(httpConfig, msq.NewSessionManager()) + server = NewServer(httpConfig, msq.NewSessionManager(msqConfig)) go server.Start(context.Background()) // wait server startup time.Sleep(time.Millisecond * 200) @@ -123,7 +122,7 @@ func TestPeek(t *testing.T) { } func TestPopWithData(t *testing.T) { - server.sm = msq.NewSessionManager() + server.sm = msq.NewSessionManager(msqConfig) popReq, _ := http.NewRequest("POST", generatePath(pop), bytes.NewBuffer(nil)) popReq.Header.Set(codec.PtpTopicID, "topic2") @@ -143,7 +142,7 @@ func TestPopWithData(t *testing.T) { outbound := verifyResponse(t, popReq, transerr.Success) assert.Equal(t, len(outbound.Payload), 10) - processTime := time.Now().Sub(start) + processTime := time.Since(start) assert.True(t, processTime >= time.Millisecond*50) } @@ -160,7 +159,7 @@ func TestPopTimeout(t *testing.T) { outbound := verifyResponse(t, popReq, transerr.Success) assert.True(t, outbound.Payload == nil) - processTime := time.Now().Sub(start) + processTime := time.Since(start) assert.Greater(t, processTime, time.Millisecond*1500) // 1.5s assert.Less(t, processTime, time.Millisecond*2500) // 2.5s } @@ -241,7 +240,7 @@ func TestPushWait(t *testing.T) { start := time.Now() verifyResponse(t, pushReq, transerr.Success) - processTime := time.Now().Sub(start) + processTime := time.Since(start) assert.Greater(t, processTime, time.Millisecond*500) // 0.5s assert.Less(t, processTime, time.Millisecond*2500) // 2.5s } @@ -256,8 +255,6 @@ func TestBadRequestParam(t *testing.T) { var sessionCount int = 10 var topicCount int = 5 -var stop bool = false - func producer(t *testing.T, sendSucceedCount, sendFailCount *int64, sessionIdx, topicIdx int) { msgLength := 256 * 1024 r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -278,7 +275,7 @@ func producer(t *testing.T, sendSucceedCount, sendFailCount *int64, sessionIdx, resp, err := client.Do(req) assert.NoError(t, err) assert.Equal(t, resp.StatusCode, 200) - body, err := io.ReadAll(resp.Body) + body, _ := io.ReadAll(resp.Body) outbound, err := server.codec.UnMarshal(body) if err == nil && outbound.Code == string(transerr.Success) { @@ -307,8 +304,8 @@ func consumer(t *testing.T, popMsgCount, popFailCount *int64, sessionIdx, topicI resp, err := client.Do(req) assert.NoError(t, err) assert.Equal(t, resp.StatusCode, 200) - body, err := io.ReadAll(resp.Body) - outbound, err := server.codec.UnMarshal(body) + body, _ := io.ReadAll(resp.Body) + outbound, _ := server.codec.UnMarshal(body) if outbound != nil && outbound.Payload != nil { atomic.AddInt64(popMsgCount, 1) @@ -361,7 +358,7 @@ func TestPerformance(t *testing.T) { sid := fmt.Sprintf("sessionx-%d", i) for j := 0; j <= topicCount; j++ { topic := fmt.Sprintf("node0-topic-%d", j) - for true { + for { msg, _ := server.sm.Peek(sid, topic) if msg == nil { break diff --git a/pkg/utils/network/grpc.go b/pkg/utils/network/grpc.go new file mode 100644 index 00000000..612a77b6 --- /dev/null +++ b/pkg/utils/network/grpc.go @@ -0,0 +1,42 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "crypto/tls" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" +) + +func BuildGrpcOptions(clientTLSConfig *tls.Config) []grpc.DialOption { + dialOpts := []grpc.DialOption{ + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 1 * time.Minute, + Timeout: 30 * time.Second, + PermitWithoutStream: true, + }), + } + if clientTLSConfig != nil { + credentials := credentials.NewTLS(clientTLSConfig) + dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials)) + } else { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } + return dialOpts +} diff --git a/pkg/utils/network/grpc_test.go b/pkg/utils/network/grpc_test.go new file mode 100644 index 00000000..576c27ba --- /dev/null +++ b/pkg/utils/network/grpc_test.go @@ -0,0 +1,26 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuildGrpcOptions(t *testing.T) { + opts := BuildGrpcOptions(nil) + assert.Len(t, opts, 2) +} diff --git a/pkg/utils/network/port_alloc.go b/pkg/utils/network/port_alloc.go new file mode 100644 index 00000000..18403b15 --- /dev/null +++ b/pkg/utils/network/port_alloc.go @@ -0,0 +1,57 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "fmt" + "net" +) + +var BuiltinPortAllocator PortAllocator + +type PortAllocator interface { + Next() (int32, error) +} + +type randomPortAllocator struct { + minPort int + maxPort int + nextCheckPort int +} + +func NewPortAllocator(hintMinPort, hintMaxPort int) PortAllocator { + return &randomPortAllocator{ + minPort: hintMinPort, + maxPort: hintMaxPort, + nextCheckPort: hintMinPort, + } +} + +func (r *randomPortAllocator) Next() (int32, error) { + for ; r.nextCheckPort < r.maxPort; r.nextCheckPort++ { + listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", r.nextCheckPort)) + if err == nil { + defer listener.Close() + r.nextCheckPort++ + return int32(r.nextCheckPort - 1), nil + } + } + + return -1, fmt.Errorf("not found a usable port in range[%d-%d)", r.minPort, r.maxPort) +} + +func init() { + BuiltinPortAllocator = NewPortAllocator(11111, 20000) +} diff --git a/pkg/utils/network/port_alloc_test.go b/pkg/utils/network/port_alloc_test.go new file mode 100644 index 00000000..edb54892 --- /dev/null +++ b/pkg/utils/network/port_alloc_test.go @@ -0,0 +1,42 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewPortAllocator(t *testing.T) { + a := NewPortAllocator(1000, 2000) + assert.NotNil(t, a) + + alloc, ok := a.(*randomPortAllocator) + assert.True(t, ok) + assert.NotNil(t, alloc) + assert.Equal(t, 1000, alloc.minPort) + assert.Equal(t, 1000, alloc.nextCheckPort) + assert.Equal(t, 2000, alloc.maxPort) +} + +func TestPortAlloc(t *testing.T) { + a := NewPortAllocator(1000, 2000) + assert.NotNil(t, a) + + port, err := a.Next() + assert.NoError(t, err) + assert.NotEqual(t, -1, int(port)) +} diff --git a/pkg/utils/resources/common.go b/pkg/utils/resources/common.go index 60b5f86a..52f84369 100644 --- a/pkg/utils/resources/common.go +++ b/pkg/utils/resources/common.go @@ -109,18 +109,18 @@ func ValidateK8sName(val string, fieldName string) error { } // IsPartnerDomain check if is partner domain. -func IsPartnerDomain(nsLister corelisters.NamespaceLister, domainID string) bool { +func IsPartnerDomain(nsLister corelisters.NamespaceLister, domainID string) (bool, error) { ns, err := nsLister.Get(domainID) if err != nil { - return false + return false, err } if ns.Labels != nil && ns.Labels[common.LabelDomainRole] == string(kusciaapisv1alpha1.Partner) { - return true + return true, nil } - return false + return false, nil } // IsEmpty will judge whether data is empty diff --git a/pkg/utils/resources/common_test.go b/pkg/utils/resources/common_test.go index a2fa2a7d..69eb18d7 100644 --- a/pkg/utils/resources/common_test.go +++ b/pkg/utils/resources/common_test.go @@ -161,7 +161,7 @@ func TestIsEmpty(t *testing.T) { } func TestSplitRSC(t *testing.T) { - input := "1000" + var input = "1000" output, err := SplitRSC(input, 5) assert.Equal(t, output, "200", "SplitRSC() function cannot handle the k8sresource.DecimalSI. ") assert.Nil(t, err, "SplitRSC() function cannot handle the k8sresource.DecimalSI. ") diff --git a/pkg/utils/resources/domaindata.go b/pkg/utils/resources/domaindata.go index ab44aef3..ae58662a 100644 --- a/pkg/utils/resources/domaindata.go +++ b/pkg/utils/resources/domaindata.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package resources import ( diff --git a/pkg/utils/resources/domaindatagrant.go b/pkg/utils/resources/domaindatagrant.go index 22f655f9..b814d407 100644 --- a/pkg/utils/resources/domaindatagrant.go +++ b/pkg/utils/resources/domaindatagrant.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package resources import ( diff --git a/pkg/utils/supervisor/supervisor.go b/pkg/utils/supervisor/supervisor.go index ac619eb2..8da2bceb 100644 --- a/pkg/utils/supervisor/supervisor.go +++ b/pkg/utils/supervisor/supervisor.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "math" + "sync" "time" "github.com/secretflow/kuscia/pkg/utils/nlog" @@ -36,6 +37,7 @@ type Cmd interface { Start() error Wait() error Pid() int + Stop() error SetOOMScore() error } @@ -71,43 +73,59 @@ func (s *Supervisor) Run(ctx context.Context, startup func(ctx context.Context) return errors.New("input startup callback is nil") } nlog.Infof("[%s] start and watch subprocess", s.tag) - isFirstRun := true + var cmd Cmd + exit := false + var lock sync.Mutex + go func() { + <-ctx.Done() + lock.Lock() + defer lock.Unlock() + if err := s.stopProcess(cmd); err != nil { + nlog.Errorf("[%s] failed to stop process, detail -> %v", s.tag, err) + } + exit = true + }() + for { isEveryTimeFailed := true s.restartIntervalIndex = 0 - for i := 0; i <= s.maxRestartCount; i++ { - nlog.Infof("[%s] try to start new process", s.tag) - if err := s.runProcess(ctx, startup(ctx)); err != nil { - nlog.Warnf("[%s] run process failed with %v", s.tag, err) - if isFirstRun { - // if first time start process failed, exit at once - return fmt.Errorf("startup process failed at first time, so stop at once, error: %v", err) - } - - isFirstRun = false - if s.restartIntervalIndex < len(s.restartIntervalMS)-1 { - s.restartIntervalIndex++ - } - } else { // process run success or longer than minRunningTimeMS - isFirstRun = false - isEveryTimeFailed = false - s.restartIntervalIndex = 0 - break - } - - // wait restart again - intervalMS := s.restartIntervalMS[s.restartIntervalIndex] - if intervalMS > 0 { - nlog.Debugf("[%s] process exited, restart again after %d ms", s.tag, intervalMS) - select { - case <-time.After(time.Duration(intervalMS * int(time.Millisecond))): - case <-ctx.Done(): - nlog.Warnf("[%s] context had done, no need to wait to restart", s.tag) + intervalMS := 0 + + for i := 0; i < s.maxRestartCount; i++ { + select { + case <-ctx.Done(): + nlog.Warnf("[%s] context had done, no need to wait to restart", s.tag) + return fmt.Errorf("context had done, no need to wait to restart") + case <-time.After(time.Duration(intervalMS * int(time.Millisecond))): + nlog.Infof("[%s] try to start new process", s.tag) + lock.Lock() + if exit { + lock.Unlock() return fmt.Errorf("context had done, no need to wait to restart") } + cmd = startup(ctx) + lock.Unlock() + if err := s.runProcess(cmd); err != nil { + nlog.Warnf("[%s] run process failed, detail -> %v", s.tag, err) + if isFirstRun { + // if first time start process failed, exit at once + return fmt.Errorf("startup process failed at first time, so stop at once, error: %v", err) + } + + isFirstRun = false + if s.restartIntervalIndex < len(s.restartIntervalMS)-1 { + s.restartIntervalIndex++ + } + } else { // process run success or longer than minRunningTimeMS + isFirstRun = false + isEveryTimeFailed = false + s.restartIntervalIndex = 0 + break + } + // wait restart again + intervalMS = s.restartIntervalMS[s.restartIntervalIndex] } - } if isEveryTimeFailed { @@ -118,29 +136,29 @@ func (s *Supervisor) Run(ctx context.Context, startup func(ctx context.Context) } } -func (s *Supervisor) runProcess(ctx context.Context, cmd Cmd) error { +func (s *Supervisor) runProcess(cmd Cmd) error { stime := time.Now() if cmd == nil { return errors.New("create subprocess failed") } if err := cmd.Start(); err != nil { - return fmt.Errorf("start process(%d) failed with %v", cmd.Pid(), err) + return fmt.Errorf("start process [%s][%d] failed with %v", s.tag, cmd.Pid(), err) } if err := cmd.SetOOMScore(); err != nil { - nlog.Warnf("Set process(%d) oom_score_adj failed, %v, skip setting it", cmd.Pid(), err) + nlog.Warnf("Set process [%s][%d] oom_score_adj failed, %v, skip setting it", s.tag, cmd.Pid(), err) } err := cmd.Wait() if err != nil { // process exit failed - nlog.Warnf("Process(%d) exit with error: %v", cmd.Pid(), err) + nlog.Warnf("Process [%s][%d] exit with error: %v", s.tag, cmd.Pid(), err) } else { - nlog.Infof("Process(%d) exit normally", cmd.Pid()) + nlog.Infof("Process [%s][%d] exit normally", s.tag, cmd.Pid()) } if dt := time.Since(stime); dt.Milliseconds() <= int64(s.minRunningTimeMS) { - tmerr := fmt.Sprintf("process(%d) only existed %d ms, less than %d ms", cmd.Pid(), dt.Milliseconds(), s.minRunningTimeMS) + tmerr := fmt.Sprintf("process [%s][%d] only existed %d ms, less than %d ms", s.tag, cmd.Pid(), dt.Milliseconds(), s.minRunningTimeMS) if err != nil { return fmt.Errorf("%s, with error: %v", tmerr, err) } @@ -149,3 +167,14 @@ func (s *Supervisor) runProcess(ctx context.Context, cmd Cmd) error { return nil } + +func (s *Supervisor) stopProcess(cmd Cmd) error { + if cmd == nil { + return nil + } + nlog.Warnf("Context done, begin to stop process [%s][%d]", s.tag, cmd.Pid()) + if err := cmd.Stop(); err != nil { + return fmt.Errorf("stop process [%s][%d] failed with %v", s.tag, cmd.Pid(), err) + } + return nil +} diff --git a/pkg/utils/supervisor/supervisor_test.go b/pkg/utils/supervisor/supervisor_test.go index 11e89411..38de8be9 100644 --- a/pkg/utils/supervisor/supervisor_test.go +++ b/pkg/utils/supervisor/supervisor_test.go @@ -66,6 +66,11 @@ func (fc *FackCmd) SetOOMScore() error { return nil } +func (fc *FackCmd) Stop() error { + nlog.Info("fack cmd.stop") + return nil +} + func newMockCmd(startMock func() error, waitMock func() error) Cmd { fack := FackCmd{ startMock: startMock, @@ -160,7 +165,7 @@ func TestSupervisorRun_MaxFailedCount(t *testing.T) { }) assert.NotNil(t, err) - assert.Equal(t, 5, count) + assert.Equal(t, 6, count) } func TestSupervisorRun_RestartAlways(t *testing.T) { diff --git a/pkg/web/constants/constants.go b/pkg/web/constants/constants.go index 8dbfeff7..91be0223 100644 --- a/pkg/web/constants/constants.go +++ b/pkg/web/constants/constants.go @@ -20,6 +20,7 @@ const ( SchemaHTTP = "http" HealthAPI = "/healthZ" TokenHeader = "Token" + XForwardHostHeader = "x-forward-host" ContentTypeHeader = "Content-Type" HTTPDefaultContentType = "application/json" SourceDomainHeader = "Kuscia-Source" diff --git a/pkg/web/decorator/proto_decorator.go b/pkg/web/decorator/proto_decorator.go index 33a98b12..2fb8038f 100644 --- a/pkg/web/decorator/proto_decorator.go +++ b/pkg/web/decorator/proto_decorator.go @@ -252,6 +252,17 @@ func InterConnProtoDecoratorMaker(validateFailedCode int32, unexpectedECode int3 } } +// CustomProtoDecoratorMaker returns custom connection ProtoDecorator. +func CustomProtoDecoratorMaker(validateFailedHandler ValidateFailedHandler, unexpectedErrorHandler UnexpectedErrorHandler) func(framework.ConfBeanRegistry, api.ProtoHandler) gin.HandlerFunc { + return func(e framework.ConfBeanRegistry, handler api.ProtoHandler) gin.HandlerFunc { + return ProtoDecorator(e, handler, &ProtoDecoratorOptions{ + ValidateFailedHandler: validateFailedHandler, + UnexpectedErrorHandler: unexpectedErrorHandler, + RenderJSONUseProtoNames: true, + }) + } +} + // setDefaultErrorResp sets default format error response with the error code. func setDefaultErrorResp(errCode int32) func(flow *BizFlow, errs *errorcode.Errs) (response api.ProtoResponse) { return func(flow *BizFlow, errs *errorcode.Errs) (response api.ProtoResponse) { diff --git a/pkg/web/errorcode/errorcode.go b/pkg/web/errorcode/error_code.go similarity index 81% rename from pkg/web/errorcode/errorcode.go rename to pkg/web/errorcode/error_code.go index b56819f8..d7d24893 100644 --- a/pkg/web/errorcode/errorcode.go +++ b/pkg/web/errorcode/error_code.go @@ -14,15 +14,17 @@ package errorcode -type KusciaErrorCode int32 +import ( + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" +) type KusciaError struct { - ErrorCode KusciaErrorCode + ErrorCode errorcode.ErrorCode Message string ErrorDetails []interface{} } -func NewKusciaError(errorCode KusciaErrorCode, message string, a ...interface{}) error { +func NewKusciaError(errorCode errorcode.ErrorCode, message string, a ...interface{}) error { return &KusciaError{ ErrorCode: errorCode, Message: message, diff --git a/pkg/web/framework/beans/gin_bean.go b/pkg/web/framework/beans/gin_bean.go index b641240d..fc98b91d 100644 --- a/pkg/web/framework/beans/gin_bean.go +++ b/pkg/web/framework/beans/gin_bean.go @@ -107,6 +107,7 @@ func (b *GinBean) Init(e framework.ConfBeanRegistry) error { func (b *GinBean) Start(ctx context.Context, e framework.ConfBeanRegistry) error { mux := http.NewServeMux() mux.Handle("/", b.Engine) + normalizeConfig(&b.GinBeanConfig) addr := fmt.Sprintf(":%d", b.Port) if b.IP != "" { @@ -120,6 +121,7 @@ func (b *GinBean) Start(ctx context.Context, e framework.ConfBeanRegistry) error MaxHeaderBytes: *b.MaxHeaderBytes, IdleTimeout: time.Duration(*b.IdleTimeout) * time.Second, } + // init server tls config if b.TLSServerConfig != nil { var err error diff --git a/pkg/web/interceptor/common.go b/pkg/web/interceptor/common.go index 305bc89d..144efa65 100644 --- a/pkg/web/interceptor/common.go +++ b/pkg/web/interceptor/common.go @@ -15,8 +15,20 @@ package interceptor import ( + "time" + "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/secretflow/kuscia/pkg/utils/nlog" +) + +var sensitiveFields = []string{"password", "access_key_id", "access_key_secret"} + +const ( + protocolGRPC = "GRPC" + protocolHTTP = "HTTP" + maxSizeBytes = 1024 ) func tokenCheck(src []string, target string) error { @@ -30,3 +42,63 @@ func tokenCheck(src []string, target string) error { } return status.Errorf(codes.Unauthenticated, "s unauthorized") } + +func safeLog(Logger nlog.NLog, protocol string, logContextFunc func()) { + defer func() { + if err := recover(); err != nil { + Logger.Errorf("Exception occurred while logging. protocol: [%s]: %v", protocol, err) + } + }() + logContextFunc() +} + +func cutSlice(slice []byte, maxLen int) []byte { + if len(slice) > maxLen { + return slice[:maxLen] + } + return slice +} + +type loggerContext struct { + Protocol string + XForwardHost string + ContextType string + Duration time.Duration + + Errs []error + + RequestPath string + RequestMethod string + RequestBody []byte + + StatusCode int + ResponseBody []byte +} + +func printfLoggerContext(logger nlog.NLog, loggerContext *loggerContext) { + + if len(loggerContext.Errs) > 0 { + logger.Errorf("[%s] [%s] Duration: %s, StatusCode: %d, ForwardHost: [%s], ContextType: [%s], Request: %s, Response: %s, Error: %v", + loggerContext.Protocol, + loggerContext.RequestPath, + loggerContext.Duration, + loggerContext.StatusCode, + loggerContext.XForwardHost, + loggerContext.ContextType, + cutSlice(loggerContext.RequestBody, maxSizeBytes), + cutSlice(loggerContext.ResponseBody, maxSizeBytes), + loggerContext.Errs) + } else { + logger.Infof("[%s] [%s %s] Duration: %s, StatusCode: %d, ForwardHost: [%s], ContextType: [%s], Request: %s, Response: %s", + loggerContext.Protocol, + loggerContext.RequestMethod, + loggerContext.RequestPath, + loggerContext.Duration, + loggerContext.StatusCode, + loggerContext.XForwardHost, + loggerContext.ContextType, + cutSlice(loggerContext.RequestBody, maxSizeBytes), + cutSlice(loggerContext.ResponseBody, maxSizeBytes), + ) + } +} diff --git a/pkg/web/interceptor/grpc_interceptor.go b/pkg/web/interceptor/grpc_interceptor.go index d06b3a71..52de6f24 100644 --- a/pkg/web/interceptor/grpc_interceptor.go +++ b/pkg/web/interceptor/grpc_interceptor.go @@ -16,14 +16,130 @@ package interceptor import ( "context" + "encoding/json" + "fmt" + "io" + "runtime/debug" "strings" + "time" "google.golang.org/grpc" + "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/web/constants" + "github.com/secretflow/kuscia/pkg/web/utils" + "github.com/secretflow/kuscia/proto/api/v1alpha1" + pberrorcode "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) +// GrpcServerLoggingInterceptor defines the unary interceptor used to log RPC requests made with a unary call. +func GrpcServerLoggingInterceptor(logger nlog.NLog) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { + startTime := time.Now() + + defer safeLog(logger, protocolGRPC, func() { + duration := time.Since(startTime) + emptyBody := make([]byte, 0) + var errors []error + + var statusCode = codes.OK + if err != nil { + errors = append(errors, err) + respStatus, _ := status.FromError(err) + statusCode = respStatus.Code() + } + + forwardHostsStr := "" + if md, ok := metadata.FromIncomingContext(ctx); ok { + forwardHosts := md.Get(constants.XForwardHostHeader) + forwardHostsStr = strings.Join(forwardHosts, ",") + } else { + logger.Warnf("[%s] Get metadata from incoming context failed", protocolGRPC) + } + + logContext := &loggerContext{ + Protocol: protocolGRPC, + RequestMethod: protocolGRPC, + RequestPath: info.FullMethod, + XForwardHost: forwardHostsStr, + StatusCode: int(statusCode), + + Errs: errors, + Duration: duration, + RequestBody: emptyBody, + ResponseBody: emptyBody, + } + + if reqByte, reqMarshalErr := json.Marshal(utils.StructToMap(req, sensitiveFields...)); reqMarshalErr != nil { + errors = append(errors, reqMarshalErr) + reqByte = emptyBody + } else { + logContext.RequestBody = reqByte + } + + if respByte, respMarshalErr := json.Marshal(utils.StructToMap(resp, sensitiveFields...)); respMarshalErr != nil { + errors = append(errors, respMarshalErr) + respByte = emptyBody + } else { + logContext.ResponseBody = respByte + } + + printfLoggerContext(logger, logContext) + }) + return handler(ctx, req) + } +} + +// GrpcStreamServerLoggingInterceptor defines the stream interceptor used to log RPC requests made with a stream. +func GrpcStreamServerLoggingInterceptor(Logger nlog.NLog) grpc.StreamServerInterceptor { + return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + Logger.Infof("[%s] Stream [%s] starting", protocolGRPC, info.FullMethod) + startTime := time.Now() + + wrappedStream := &loggingServerStream{ServerStream: ss} + var err = handler(srv, wrappedStream) + + defer safeLog(Logger, protocolGRPC, func() { + duration := time.Since(startTime) + + recvMsgJSON, _ := json.Marshal(utils.StructToMap(wrappedStream.recvMsg, sensitiveFields...)) + sendMsgJSON, _ := json.Marshal(utils.StructToMap(wrappedStream.sendMsg, sensitiveFields...)) + recvMsgJSON = cutSlice(recvMsgJSON, maxSizeBytes) + sendMsgJSON = cutSlice(sendMsgJSON, maxSizeBytes) + if err != nil { + Logger.Errorf("[%s] Stream [%s] finished with error after %s, recvMsg: %s: %v", protocolGRPC, info.FullMethod, duration, recvMsgJSON, err) + } else { + Logger.Infof("[%s] Stream [%s] finished successfully after %s. recvMsg: %s, sendMsg: %s", protocolGRPC, info.FullMethod, duration, recvMsgJSON, sendMsgJSON) + } + }) + return err + } +} + +type loggingServerStream struct { + grpc.ServerStream + sendMsg any + recvMsg any +} + +// SendMsg Wrapped the method of sending messages to record the send messages. +func (s *loggingServerStream) SendMsg(m interface{}) error { + s.sendMsg = m + return s.ServerStream.SendMsg(m) +} + +// RecvMsg Wrapped the method of receiving messages to record the received messages. +func (s *loggingServerStream) RecvMsg(m interface{}) error { + err := s.ServerStream.RecvMsg(m) + if err == nil || err == io.EOF { + s.recvMsg = m + } + return err +} + func GrpcServerTokenInterceptor(tokenData string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { tokens := metadata.ValueFromIncomingContext(ctx, strings.ToLower(constants.TokenHeader)) @@ -70,3 +186,45 @@ func GrpcClientTokenInterceptor(tokenData string) grpc.UnaryClientInterceptor { return invoker(ctx, method, req, reply, cc, opts...) } } + +// UnaryRecoverInterceptor returns a new unary server interceptors that recovers from panics. +func UnaryRecoverInterceptor(errorCode pberrorcode.ErrorCode) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + defer func() { + if r := recover(); r != nil { + nlog.Errorf("[%s] Recovered from panic: %+v, stack: %s", info.FullMethod, r, debug.Stack()) + wrappedErr := fmt.Errorf("%s", r) + resp = &v1alpha1.ErrorResponse{ + Status: &v1alpha1.Status{ + Code: int32(errorCode), + Message: wrappedErr.Error(), + }, + } + } + }() + + resp, err = handler(ctx, req) + return resp, err + } +} + +// StreamRecoverInterceptor returns a new stream server interceptors that recovers from panics. +func StreamRecoverInterceptor(errorCode pberrorcode.ErrorCode) grpc.StreamServerInterceptor { + return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) { + defer func() { + if r := recover(); r != nil { + nlog.Errorf("[%s] Recovered from panic: %+v, stack: %s", info.FullMethod, r, debug.Stack()) + wrappedErr := fmt.Errorf("%s", r) + resp := &v1alpha1.ErrorResponse{ + Status: &v1alpha1.Status{ + Code: int32(errorCode), + Message: wrappedErr.Error(), + }, + } + _ = ss.SendMsg(resp) + } + }() + err = handler(srv, ss) + return err + } +} diff --git a/pkg/web/interceptor/http_interceptor.go b/pkg/web/interceptor/http_interceptor.go index 20087f17..581277af 100644 --- a/pkg/web/interceptor/http_interceptor.go +++ b/pkg/web/interceptor/http_interceptor.go @@ -15,15 +15,129 @@ package interceptor import ( + "bytes" + "encoding/json" + "fmt" + "io" "net/http" + "time" "github.com/gin-gonic/gin" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/secretflow/kuscia/pkg/utils/nlog" "github.com/secretflow/kuscia/pkg/web/constants" + "github.com/secretflow/kuscia/pkg/web/utils" ) +type responseWithBodyWriter struct { + gin.ResponseWriter + body *bytes.Buffer +} + +// Write Override the original method to capture response body data +func (w *responseWithBodyWriter) Write(b []byte) (int, error) { + w.body.Write(b) + return w.ResponseWriter.Write(b) +} + +// WriteString Override the original method to capture response body data +func (w *responseWithBodyWriter) WriteString(s string) (int, error) { + w.body.WriteString(s) + return w.ResponseWriter.WriteString(s) +} + +// ResponseBody Return the captured response body +func (w *responseWithBodyWriter) ResponseBody() string { + return w.body.String() +} + +func newResponseWithBodyWriter(rw gin.ResponseWriter) *responseWithBodyWriter { + return &responseWithBodyWriter{ + ResponseWriter: rw, + body: &bytes.Buffer{}, + } +} + +func HTTPServerLoggingInterceptor(logger nlog.NLog) func(c *gin.Context) { + return func(c *gin.Context) { + var r = c.Request + withBodyWriter := newResponseWithBodyWriter(c.Writer) + c.Writer = withBodyWriter + // log prefix : request method and URI + prefix := r.Method + " " + r.URL.RequestURI() + // request Body + var requestBody = make([]byte, 0) + if r.Body != nil && r.ContentLength < maxSizeBytes { + var err error + if requestBody, err = io.ReadAll(r.Body); err != nil { + _ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("[%s] read request body failed: %v", prefix, err)) + return + } + r.Body = io.NopCloser(bytes.NewBuffer(requestBody)) + } + // request start time + startTime := time.Now() + defer safeLog(logger, protocolHTTP, func() { + // request processing time + duration := time.Since(startTime) + var errors []error + emptyBody := make([]byte, 0) + + if c.Err() != nil { + errors = append(errors, c.Err()) + } + + context := &loggerContext{ + Protocol: protocolHTTP, + RequestMethod: r.Method, + RequestPath: r.URL.RequestURI(), + XForwardHost: r.Header.Get(constants.XForwardHostHeader), + ContextType: c.Writer.Header().Get(constants.ContentTypeHeader), + StatusCode: c.Writer.Status(), + Duration: duration, + Errs: errors, + RequestBody: emptyBody, + ResponseBody: emptyBody, + } + + var structInterface any + // request body + if len(requestBody) > 0 { + if unmarshalReqErr := json.Unmarshal(requestBody, &structInterface); unmarshalReqErr != nil { + errors = append(errors, unmarshalReqErr) + } else { + requestMap := utils.StructToMap(structInterface, sensitiveFields...) + if requestJSON, marshalReqErr := json.Marshal(requestMap); marshalReqErr != nil { + errors = append(errors, marshalReqErr) + } else { + context.RequestBody = requestJSON + } + } + } + // response body + responseBodyLen := withBodyWriter.body.Len() + logger.Debugf("response body len: %d", withBodyWriter.body.Len()) + if responseBodyLen < maxSizeBytes { + if unmarshalRespErr := json.Unmarshal([]byte(withBodyWriter.ResponseBody()), &structInterface); unmarshalRespErr != nil { + errors = append(errors, unmarshalRespErr) + } else { + responseMap := utils.StructToMap(structInterface, sensitiveFields...) + if responseJSON, marshalRespErr := json.Marshal(responseMap); marshalRespErr != nil { + errors = append(errors, marshalRespErr) + } else { + context.ResponseBody = responseJSON + } + } + } + printfLoggerContext(logger, context) + }) + // run next handler + c.Next() + } +} + func HTTPTokenAuthInterceptor(tokenData string) func(c *gin.Context) { return func(c *gin.Context) { token := c.GetHeader(constants.TokenHeader) diff --git a/pkg/web/utils/desensitization.go b/pkg/web/utils/desensitization.go new file mode 100644 index 00000000..a683cccf --- /dev/null +++ b/pkg/web/utils/desensitization.go @@ -0,0 +1,170 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "reflect" + "strings" + + "github.com/secretflow/kuscia/pkg/utils/nlog" +) + +const desensitizedValue = "***" + +// StructToMap is a function that converts a struct to a map with sensitive data obfuscated. +func StructToMap(s interface{}, sensitiveFields ...string) map[string]interface{} { + if s == nil { + return map[string]interface{}{} + } + return structToMap(s, getFieldTypeNameWithPbJSONTag, sensitiveFields...) +} + +type fieldTypeNameFunc func(t reflect.StructField) string + +func getFieldTypeNameWithPbJSONTag(fieldType reflect.StructField) string { + jsonTag := fieldType.Tag.Get("json") + protobufTag := fieldType.Tag.Get("protobuf") + + if jsonTag == "" || jsonTag == "-" || protobufTag == "" { + return "" + } + return strings.Split(jsonTag, ",")[0] +} + +func structToMap(s any, nameFunc fieldTypeNameFunc, sensitiveFields ...string) map[string]interface{} { + var result = map[string]interface{}{} + // Get the reflection value of a struct. + value := reflect.ValueOf(s) + // Get the reflection type of struct. + var typeof reflect.Type + if value.Kind() == reflect.Pointer { + value = value.Elem() + if !value.IsValid() { + return result + } + } + typeof = value.Type() + + if typeof.Kind() == reflect.Map { + for _, key := range value.MapKeys() { + keyStr := key.String() + val := value.MapIndex(key).Interface() + result[keyStr] = val + } + filterMapDesensitizeKey(result, nameFunc, sensitiveFields) + return result + } + + if typeof.Kind() != reflect.Struct { + return result + } + // Traverse all fields of a struct. + for i := 0; i < typeof.NumField(); i++ { + // Get the reflection value of a field. + fieldValue := value.Field(i) + // Get the reflection type of field. + fieldType := typeof.Field(i) + fieldName := nameFunc(fieldType) + fieldTypeKind := fieldType.Type.Kind() + + if fieldName == "" { + continue + } + if fieldTypeKind == reflect.Pointer { + fieldValue = fieldValue.Elem() + fieldTypeKind = fieldType.Type.Elem().Kind() + } + + if !fieldValue.IsValid() { + continue + } + fieldInterface := fieldValue.Interface() + switch fieldTypeKind { + case reflect.Struct: + result[fieldName] = structToMap(fieldInterface, nameFunc, sensitiveFields...) + case reflect.Map: + if stringMap, ok := fieldInterface.(map[string]string); ok { + interfaceMap := make(map[string]interface{}) + for key, value := range stringMap { + interfaceMap[key] = value + } + result[fieldName] = filterMapDesensitizeKey(interfaceMap, nameFunc, sensitiveFields) + } else if interfaceMap, ok := fieldInterface.(map[string]interface{}); ok { + result[fieldName] = filterMapDesensitizeKey(interfaceMap, nameFunc, sensitiveFields) + } else { + nlog.Warnf("FieldName: %s, can not transform to `map[string]string` or `map[string]interface{}`: %v", fieldName, fieldInterface) + } + case reflect.String: + if str := desensitizeFieldOrKeyValue(fieldName, fieldInterface, sensitiveFields); str != "" { + result[fieldName] = str + } + case reflect.Slice: + interfaceMap := map[string]interface{}{ + fieldName: fieldInterface, + } + result[fieldName] = filterMapDesensitizeKey(interfaceMap, nameFunc, sensitiveFields)[fieldName] + default: + result[fieldName] = desensitizeFieldOrKeyValue(fieldName, fieldInterface, sensitiveFields) + } + } + return result +} + +func filterMapDesensitizeKey(mapData map[string]interface{}, nameFunc fieldTypeNameFunc, sensitiveFields []string) map[string]interface{} { + if len(sensitiveFields) == 0 { + return mapData + } + for key, val := range mapData { + + switch value := val.(type) { + case string: + if value != "" { + mapData[key] = desensitizeFieldOrKeyValue(key, value, sensitiveFields) + } + case map[string]interface{}: + mapData[key] = filterMapDesensitizeKey(value, nameFunc, sensitiveFields) + case []interface{}: + var newSubSlice []map[string]interface{} + for _, sliceValue := range value { + newSubSlice = append(newSubSlice, structToMap(sliceValue, nameFunc, sensitiveFields...)) + } + mapData[key] = newSubSlice + case []map[string]interface{}: + var newSubSlice []map[string]interface{} + for _, sliceValue := range value { + newSubSlice = append(newSubSlice, filterMapDesensitizeKey(sliceValue, nameFunc, sensitiveFields)) + } + mapData[key] = newSubSlice + default: + if reflect.ValueOf(val).Kind() == reflect.Struct { + mapData[key] = structToMap(val, nameFunc, sensitiveFields...) + } else { + mapData[key] = desensitizeFieldOrKeyValue(key, value, sensitiveFields) + } + } + } + return mapData +} + +func desensitizeFieldOrKeyValue(fieldOrKeyName string, v any, sensitiveFields []string) any { + fieldInterface := v + for _, sensitiveField := range sensitiveFields { + if sensitiveField == fieldOrKeyName { + fieldInterface = desensitizedValue + break + } + } + return fieldInterface +} diff --git a/pkg/web/utils/desensitization_test.go b/pkg/web/utils/desensitization_test.go new file mode 100644 index 00000000..a94190d6 --- /dev/null +++ b/pkg/web/utils/desensitization_test.go @@ -0,0 +1,250 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/secretflow/kuscia/proto/api/v1alpha1" + "github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi" +) + +type testStruct struct { + Name string + Age int + Address string + Password string `protobuf:"bytes,2,opt,name=password,json=password,proto3" json:"password,omitempty"` + Number1 int `protobuf:"bytes,2,opt,name=number1,json=number1,proto3" json:"number1,omitempty"` + Map map[string]interface{} `protobuf:"bytes,2,opt,name=map,json=map,proto3" json:"map,omitempty"` + Slice []int `protobuf:"bytes,2,opt,name=slice,json=slice,proto3" json:"slice,omitempty"` +} + +func TestStructToMap_Pb(t *testing.T) { + + dataSourceName := "test" + + createDomainDataSourceRequest := kusciaapi.CreateDomainDataSourceRequest{ + Header: nil, + DomainId: "", + Name: &dataSourceName, + Info: &kusciaapi.DataSourceInfo{ + Oss: &kusciaapi.OssDataSourceInfo{ + Endpoint: "endpoint", + Bucket: "bucket", + AccessKeyId: "accessKeyId", + AccessKeySecret: "accessKeySecret", + }, + }, + } + var result = StructToMap(&createDomainDataSourceRequest, "bucket", "access_key_id", "access_key_secret") + + expected := map[string]interface{}{ + "name": dataSourceName, + "info": map[string]interface{}{ + "oss": map[string]interface{}{ + "endpoint": "endpoint", + "bucket": desensitizedValue, + "access_key_id": desensitizedValue, + "access_key_secret": desensitizedValue, + "virtualhost": false, + }, + }, + } + assert.EqualValues(t, result, expected) +} +func TestStructToMap_Map(t *testing.T) { + + var m = make(map[string]interface{}) + m["a"] = 1 + m["b"] = 2 + m["c"] = 3 + + var result = StructToMap(m, "b") + expected := map[string]interface{}{ + "a": 1, + "b": desensitizedValue, + "c": 3, + } + assert.EqualValues(t, result, expected) +} +func TestStructToMap_MapInterface(t *testing.T) { + + var m = make(map[string]interface{}) + m["a"] = 1 + m["b"] = map[string]string{ + "d": "d", + "e": "e", + } + m["c"] = 3 + + var result = StructToMap(m, "b", "d") + expected := map[string]interface{}{ + "a": 1, + "b": desensitizedValue, + "c": 3, + } + assert.EqualValues(t, result, expected) +} + +func TestStructToMap_MapArray(t *testing.T) { + + var m = make(map[string]interface{}) + m["a"] = 1 + m["b"] = []map[string]interface{}{ + { + "d": "d", + "e": "e", + }, + } + m["c"] = 3 + + var result = StructToMap(m, "d") + expected := map[string]interface{}{ + "a": 1, + "b": []map[string]interface{}{ + { + "d": desensitizedValue, + "e": "e", + }, + }, + "c": 3, + } + assert.EqualValues(t, result, expected) +} + +func TestStructToMap_MapStruct(t *testing.T) { + dataSourceName := "test" + + m := map[string]interface{}{ + "a": 1, + "b": []map[string]interface{}{ + { + "d": "d", + "e": "e", + }, + }, + "c": kusciaapi.CreateDomainDataSourceRequest{ + Header: nil, + DomainId: "", + Name: &dataSourceName, + Info: &kusciaapi.DataSourceInfo{ + Oss: &kusciaapi.OssDataSourceInfo{ + Endpoint: "endpoint", + Bucket: "bucket", + AccessKeyId: "accessKeyId", + AccessKeySecret: "accessKeySecret", + }, + }, + }, + } + + var result = StructToMap(m, "d", "bucket", "access_key_id", "access_key_secret") + expected := map[string]interface{}{ + "a": 1, + "b": []map[string]interface{}{ + { + "d": desensitizedValue, + "e": "e", + }, + }, + "c": map[string]interface{}{ + "name": dataSourceName, + "info": map[string]interface{}{ + "oss": map[string]interface{}{ + "endpoint": "endpoint", + "bucket": desensitizedValue, + "access_key_id": desensitizedValue, + "access_key_secret": desensitizedValue, + "virtualhost": false, + }, + }, + }, + } + assert.EqualValues(t, result, expected) +} +func TestStructToMap_StructMap(t *testing.T) { + myStruct := testStruct{ + Name: "John", + Age: 30, + Address: "123 Main St", + Password: "password", + Map: map[string]interface{}{ + "a": "1", + "b": "2", + }, + Slice: make([]int, 0), + } + + result := StructToMap(myStruct, "password", "a") + expected := map[string]interface{}{ + "password": desensitizedValue, + "number1": 0, + "map": map[string]interface{}{ + "a": desensitizedValue, + "b": "2", + }, + "slice": []int{}, + } + assert.EqualValues(t, result, expected) +} + +func TestStructToMap_PbStream(t *testing.T) { + + requestHeader := v1alpha1.RequestHeader{ + CustomHeaders: map[string]string{ + "hello": "hello", + }, + } + request := kusciaapi.WatchJobRequest{ + TimeoutSeconds: 10, + Header: &requestHeader, + } + + var result = StructToMap(&request, "bucket", "access_key_id", "access_key_secret") + + expected := map[string]interface{}{ + "header": map[string]interface{}{ + "custom_headers": map[string]interface{}{ + "hello": "hello", + }, + }, + "timeout_seconds": int64(10), + } + assert.EqualValues(t, result, expected) + +} + +func TestStructToMap_StructWithNumberSlice(t *testing.T) { + myStruct := testStruct{ + Name: "John", + Age: 30, + Address: "123 Main St", + Password: "password", + Number1: 1, + Slice: []int{1, 2, 3}, + Map: map[string]interface{}{}, + } + + result := StructToMap(myStruct, "password", "number1") + expected := map[string]interface{}{ + "password": desensitizedValue, + "number1": desensitizedValue, + "slice": []int{1, 2, 3}, + "map": map[string]interface{}{}, + } + assert.EqualValues(t, result, expected) +} diff --git a/pkg/web/utils/response_status.go b/pkg/web/utils/response_status.go index e41e4dbb..710153d1 100644 --- a/pkg/web/utils/response_status.go +++ b/pkg/web/utils/response_status.go @@ -15,15 +15,11 @@ package utils import ( - "github.com/secretflow/kuscia/pkg/web/errorcode" "github.com/secretflow/kuscia/proto/api/v1alpha1" + "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode" ) -const ( - ResponseCodeSuccess = 0 -) - -func BuildErrorResponseStatus(errCode errorcode.KusciaErrorCode, msg string) *v1alpha1.Status { +func BuildErrorResponseStatus(errCode errorcode.ErrorCode, msg string) *v1alpha1.Status { return &v1alpha1.Status{ Code: int32(errCode), Message: msg, @@ -32,7 +28,11 @@ func BuildErrorResponseStatus(errCode errorcode.KusciaErrorCode, msg string) *v1 func BuildSuccessResponseStatus() *v1alpha1.Status { return &v1alpha1.Status{ - Code: ResponseCodeSuccess, + Code: int32(errorcode.ErrorCode_SUCCESS), Message: "success", } } + +func IsSuccessCode(code int32) bool { + return code == int32(errorcode.ErrorCode_SUCCESS) +} diff --git a/proto/api/v1alpha1/common.pb.go b/proto/api/v1alpha1/common.pb.go index 940c304f..8912e545 100644 --- a/proto/api/v1alpha1/common.pb.go +++ b/proto/api/v1alpha1/common.pb.go @@ -333,6 +333,53 @@ func (x *DataColumn) GetNotNullable() bool { return false } +type ErrorResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status *Status `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *ErrorResponse) Reset() { + *x = ErrorResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_kuscia_proto_api_v1alpha1_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ErrorResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ErrorResponse) ProtoMessage() {} + +func (x *ErrorResponse) ProtoReflect() protoreflect.Message { + mi := &file_kuscia_proto_api_v1alpha1_common_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ErrorResponse.ProtoReflect.Descriptor instead. +func (*ErrorResponse) Descriptor() ([]byte, []int) { + return file_kuscia_proto_api_v1alpha1_common_proto_rawDescGZIP(), []int{4} +} + +func (x *ErrorResponse) GetStatus() *Status { + if x != nil { + return x.Status + } + return nil +} + var File_kuscia_proto_api_v1alpha1_common_proto protoreflect.FileDescriptor var file_kuscia_proto_api_v1alpha1_common_proto_rawDesc = []byte{ @@ -373,15 +420,20 @@ var file_kuscia_proto_api_v1alpha1_common_proto_rawDesc = []byte{ 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x6f, 0x74, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, - 0x65, 0x2a, 0x2e, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, - 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, - 0x43, 0x53, 0x56, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, - 0x02, 0x42, 0x51, 0x0a, 0x1e, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, - 0x6c, 0x6f, 0x77, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x6b, 0x75, 0x73, 0x63, 0x69, - 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x22, 0x4a, 0x0a, 0x0d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a, 0x2e, 0x0a, + 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x53, 0x56, 0x10, + 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x02, 0x42, 0x51, 0x0a, + 0x1e, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x5a, + 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -397,25 +449,27 @@ func file_kuscia_proto_api_v1alpha1_common_proto_rawDescGZIP() []byte { } var file_kuscia_proto_api_v1alpha1_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_kuscia_proto_api_v1alpha1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_kuscia_proto_api_v1alpha1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_kuscia_proto_api_v1alpha1_common_proto_goTypes = []interface{}{ (FileFormat)(0), // 0: kuscia.proto.api.v1alpha1.FileFormat (*RequestHeader)(nil), // 1: kuscia.proto.api.v1alpha1.RequestHeader (*Status)(nil), // 2: kuscia.proto.api.v1alpha1.Status (*Partition)(nil), // 3: kuscia.proto.api.v1alpha1.Partition (*DataColumn)(nil), // 4: kuscia.proto.api.v1alpha1.DataColumn - nil, // 5: kuscia.proto.api.v1alpha1.RequestHeader.CustomHeadersEntry - (*anypb.Any)(nil), // 6: google.protobuf.Any + (*ErrorResponse)(nil), // 5: kuscia.proto.api.v1alpha1.ErrorResponse + nil, // 6: kuscia.proto.api.v1alpha1.RequestHeader.CustomHeadersEntry + (*anypb.Any)(nil), // 7: google.protobuf.Any } var file_kuscia_proto_api_v1alpha1_common_proto_depIdxs = []int32{ - 5, // 0: kuscia.proto.api.v1alpha1.RequestHeader.custom_headers:type_name -> kuscia.proto.api.v1alpha1.RequestHeader.CustomHeadersEntry - 6, // 1: kuscia.proto.api.v1alpha1.Status.details:type_name -> google.protobuf.Any + 6, // 0: kuscia.proto.api.v1alpha1.RequestHeader.custom_headers:type_name -> kuscia.proto.api.v1alpha1.RequestHeader.CustomHeadersEntry + 7, // 1: kuscia.proto.api.v1alpha1.Status.details:type_name -> google.protobuf.Any 4, // 2: kuscia.proto.api.v1alpha1.Partition.fields:type_name -> kuscia.proto.api.v1alpha1.DataColumn - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 2, // 3: kuscia.proto.api.v1alpha1.ErrorResponse.status:type_name -> kuscia.proto.api.v1alpha1.Status + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_kuscia_proto_api_v1alpha1_common_proto_init() } @@ -472,6 +526,18 @@ func file_kuscia_proto_api_v1alpha1_common_proto_init() { return nil } } + file_kuscia_proto_api_v1alpha1_common_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ErrorResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -479,7 +545,7 @@ func file_kuscia_proto_api_v1alpha1_common_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_kuscia_proto_api_v1alpha1_common_proto_rawDesc, NumEnums: 1, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/api/v1alpha1/common.proto b/proto/api/v1alpha1/common.proto index f3365449..ae6275e6 100644 --- a/proto/api/v1alpha1/common.proto +++ b/proto/api/v1alpha1/common.proto @@ -61,3 +61,7 @@ enum FileFormat { CSV = 1; BINARY = 2; } + +message ErrorResponse { + Status status = 1; +} \ No newline at end of file diff --git a/proto/api/v1alpha1/datamesh/flightdm.pb.go b/proto/api/v1alpha1/datamesh/flightdm.pb.go index 340152d7..2cf1e3ac 100644 --- a/proto/api/v1alpha1/datamesh/flightdm.pb.go +++ b/proto/api/v1alpha1/datamesh/flightdm.pb.go @@ -416,538 +416,6 @@ func (x *CommandDomainDataUpdate) GetExtraOptions() map[string]string { return nil } -type TicketDomainDataQuery struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // a unique identifier related to a DomainData query - DomaindataHandle string `protobuf:"bytes,1,opt,name=domaindata_handle,json=domaindataHandle,proto3" json:"domaindata_handle,omitempty"` -} - -func (x *TicketDomainDataQuery) Reset() { - *x = TicketDomainDataQuery{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TicketDomainDataQuery) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TicketDomainDataQuery) ProtoMessage() {} - -func (x *TicketDomainDataQuery) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TicketDomainDataQuery.ProtoReflect.Descriptor instead. -func (*TicketDomainDataQuery) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{5} -} - -func (x *TicketDomainDataQuery) GetDomaindataHandle() string { - if x != nil { - return x.DomaindataHandle - } - return "" -} - -// call DoAction with ActionCreateDomainDataRequest, return ActionCreateDomainDataResponse -type ActionCreateDomainDataRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Request *CreateDomainDataRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` -} - -func (x *ActionCreateDomainDataRequest) Reset() { - *x = ActionCreateDomainDataRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionCreateDomainDataRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionCreateDomainDataRequest) ProtoMessage() {} - -func (x *ActionCreateDomainDataRequest) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionCreateDomainDataRequest.ProtoReflect.Descriptor instead. -func (*ActionCreateDomainDataRequest) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{6} -} - -func (x *ActionCreateDomainDataRequest) GetRequest() *CreateDomainDataRequest { - if x != nil { - return x.Request - } - return nil -} - -type ActionCreateDomainDataResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Response *CreateDomainDataResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` -} - -func (x *ActionCreateDomainDataResponse) Reset() { - *x = ActionCreateDomainDataResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionCreateDomainDataResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionCreateDomainDataResponse) ProtoMessage() {} - -func (x *ActionCreateDomainDataResponse) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionCreateDomainDataResponse.ProtoReflect.Descriptor instead. -func (*ActionCreateDomainDataResponse) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{7} -} - -func (x *ActionCreateDomainDataResponse) GetResponse() *CreateDomainDataResponse { - if x != nil { - return x.Response - } - return nil -} - -// call DoAction with ActionQueryDomainDataRequest, return ActionQueryDomainDataResponse -type ActionQueryDomainDataRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Request *QueryDomainDataRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` -} - -func (x *ActionQueryDomainDataRequest) Reset() { - *x = ActionQueryDomainDataRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionQueryDomainDataRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionQueryDomainDataRequest) ProtoMessage() {} - -func (x *ActionQueryDomainDataRequest) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionQueryDomainDataRequest.ProtoReflect.Descriptor instead. -func (*ActionQueryDomainDataRequest) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{8} -} - -func (x *ActionQueryDomainDataRequest) GetRequest() *QueryDomainDataRequest { - if x != nil { - return x.Request - } - return nil -} - -type ActionQueryDomainDataResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Response *QueryDomainDataResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` -} - -func (x *ActionQueryDomainDataResponse) Reset() { - *x = ActionQueryDomainDataResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionQueryDomainDataResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionQueryDomainDataResponse) ProtoMessage() {} - -func (x *ActionQueryDomainDataResponse) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionQueryDomainDataResponse.ProtoReflect.Descriptor instead. -func (*ActionQueryDomainDataResponse) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{9} -} - -func (x *ActionQueryDomainDataResponse) GetResponse() *QueryDomainDataResponse { - if x != nil { - return x.Response - } - return nil -} - -// call DoAction with ActionUpdateDomainDataRequest, return ActionUpdateDomainDataResponse -type ActionUpdateDomainDataRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Request *UpdateDomainDataRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` -} - -func (x *ActionUpdateDomainDataRequest) Reset() { - *x = ActionUpdateDomainDataRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionUpdateDomainDataRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionUpdateDomainDataRequest) ProtoMessage() {} - -func (x *ActionUpdateDomainDataRequest) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionUpdateDomainDataRequest.ProtoReflect.Descriptor instead. -func (*ActionUpdateDomainDataRequest) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{10} -} - -func (x *ActionUpdateDomainDataRequest) GetRequest() *UpdateDomainDataRequest { - if x != nil { - return x.Request - } - return nil -} - -type ActionUpdateDomainDataResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Response *UpdateDomainDataResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` -} - -func (x *ActionUpdateDomainDataResponse) Reset() { - *x = ActionUpdateDomainDataResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionUpdateDomainDataResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionUpdateDomainDataResponse) ProtoMessage() {} - -func (x *ActionUpdateDomainDataResponse) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionUpdateDomainDataResponse.ProtoReflect.Descriptor instead. -func (*ActionUpdateDomainDataResponse) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{11} -} - -func (x *ActionUpdateDomainDataResponse) GetResponse() *UpdateDomainDataResponse { - if x != nil { - return x.Response - } - return nil -} - -// call DoAction with ActionDeleteDomainDataRequest, return ActionDeleteDomainDataResponse -type ActionDeleteDomainDataRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Request *DeleteDomainDataRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` - // not supported now - PhysicalDeletion bool `protobuf:"varint,2,opt,name=physical_deletion,json=physicalDeletion,proto3" json:"physical_deletion,omitempty"` -} - -func (x *ActionDeleteDomainDataRequest) Reset() { - *x = ActionDeleteDomainDataRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionDeleteDomainDataRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionDeleteDomainDataRequest) ProtoMessage() {} - -func (x *ActionDeleteDomainDataRequest) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionDeleteDomainDataRequest.ProtoReflect.Descriptor instead. -func (*ActionDeleteDomainDataRequest) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{12} -} - -func (x *ActionDeleteDomainDataRequest) GetRequest() *DeleteDomainDataRequest { - if x != nil { - return x.Request - } - return nil -} - -func (x *ActionDeleteDomainDataRequest) GetPhysicalDeletion() bool { - if x != nil { - return x.PhysicalDeletion - } - return false -} - -type ActionDeleteDomainDataResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Response *DeleteDomainDataResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` -} - -func (x *ActionDeleteDomainDataResponse) Reset() { - *x = ActionDeleteDomainDataResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionDeleteDomainDataResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionDeleteDomainDataResponse) ProtoMessage() {} - -func (x *ActionDeleteDomainDataResponse) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionDeleteDomainDataResponse.ProtoReflect.Descriptor instead. -func (*ActionDeleteDomainDataResponse) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{13} -} - -func (x *ActionDeleteDomainDataResponse) GetResponse() *DeleteDomainDataResponse { - if x != nil { - return x.Response - } - return nil -} - -// call DoAction with ActionQueryDomainDataSourceRequest, return ActionQueryDomainDataSourceResponse -type ActionQueryDomainDataSourceRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Request *QueryDomainDataSourceRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` -} - -func (x *ActionQueryDomainDataSourceRequest) Reset() { - *x = ActionQueryDomainDataSourceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionQueryDomainDataSourceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionQueryDomainDataSourceRequest) ProtoMessage() {} - -func (x *ActionQueryDomainDataSourceRequest) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionQueryDomainDataSourceRequest.ProtoReflect.Descriptor instead. -func (*ActionQueryDomainDataSourceRequest) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{14} -} - -func (x *ActionQueryDomainDataSourceRequest) GetRequest() *QueryDomainDataSourceRequest { - if x != nil { - return x.Request - } - return nil -} - -type ActionQueryDomainDataSourceResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Response *QueryDomainDataSourceResponse `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` -} - -func (x *ActionQueryDomainDataSourceResponse) Reset() { - *x = ActionQueryDomainDataSourceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionQueryDomainDataSourceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionQueryDomainDataSourceResponse) ProtoMessage() {} - -func (x *ActionQueryDomainDataSourceResponse) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionQueryDomainDataSourceResponse.ProtoReflect.Descriptor instead. -func (*ActionQueryDomainDataSourceResponse) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP(), []int{15} -} - -func (x *ActionQueryDomainDataSourceResponse) GetResponse() *QueryDomainDataSourceResponse { - if x != nil { - return x.Response - } - return nil -} - var File_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto protoreflect.FileDescriptor var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDesc = []byte{ @@ -959,54 +427,28 @@ var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDesc = []byte{ 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x1a, 0x33, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x39, 0x6b, 0x75, - 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, - 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3a, 0x0a, 0x0f, 0x43, 0x53, 0x56, 0x57, 0x72, - 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x65, 0x72, 0x22, 0x75, 0x0a, 0x10, 0x46, 0x69, 0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x56, 0x0a, 0x0b, 0x63, 0x73, 0x76, 0x5f, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6b, - 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x43, 0x53, 0x56, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x73, 0x76, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, - 0x09, 0x0a, 0x07, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x41, 0x0a, 0x1a, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, - 0x74, 0x61, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x49, 0x64, 0x22, 0x8f, 0x02, - 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, - 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x49, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, - 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x52, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, - 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, - 0x73, 0x68, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x62, 0x0a, 0x12, 0x66, - 0x69, 0x6c, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x66, - 0x69, 0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x97, 0x04, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x49, 0x64, - 0x12, 0x6a, 0x0a, 0x12, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6b, - 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x11, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0c, + 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3a, 0x0a, 0x0f, + 0x43, 0x53, 0x56, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, + 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x22, 0x75, 0x0a, 0x10, 0x46, 0x69, 0x6c, 0x65, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x56, 0x0a, 0x0b, + 0x63, 0x73, 0x76, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x33, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, + 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x43, 0x53, 0x56, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x73, 0x76, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x09, 0x0a, 0x07, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x41, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x23, 0x0a, + 0x0d, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, + 0x49, 0x64, 0x22, 0x8f, 0x02, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x23, 0x0a, + 0x0d, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, + 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x12, 0x52, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, @@ -1018,112 +460,49 @@ var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDesc = []byte{ 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x72, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4d, 0x2e, 0x6b, 0x75, - 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, - 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, - 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x72, - 0x61, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3f, 0x0a, 0x11, 0x45, 0x78, 0x74, 0x72, - 0x61, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x44, 0x0a, 0x15, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x22, - 0x76, 0x0a, 0x1d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x55, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x97, 0x04, 0x0a, 0x17, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, + 0x61, 0x74, 0x61, 0x49, 0x64, 0x12, 0x6a, 0x0a, 0x12, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, + 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7a, 0x0a, 0x1e, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x08, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x6b, 0x75, - 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x74, 0x0a, 0x1c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x54, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x78, 0x0a, 0x1d, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6b, - 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x76, 0x0a, 0x1d, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x55, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7a, 0x0a, 0x1e, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x3c, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, - 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa3, 0x01, 0x0a, 0x1d, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x55, 0x0a, 0x07, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6b, 0x75, 0x73, - 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x70, 0x68, 0x79, - 0x73, 0x69, 0x63, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, - 0x1e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x58, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x3c, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x80, 0x01, 0x0a, 0x22, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, - 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x5a, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x40, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x11, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x52, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x62, 0x0a, 0x12, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x34, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x84, 0x01, 0x0a, - 0x23, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2a, 0x2a, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x00, 0x12, 0x07, 0x0a, - 0x03, 0x52, 0x41, 0x57, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x53, 0x56, 0x10, 0x02, 0x42, - 0x5c, 0x0a, 0x20, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, 0x6c, 0x6f, - 0x77, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, - 0x65, 0x73, 0x68, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x6b, 0x75, 0x73, 0x63, 0x69, - 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x72, 0x0a, 0x0d, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x4d, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, + 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x45, 0x78, + 0x74, 0x72, 0x61, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0c, 0x65, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3f, 0x0a, + 0x11, 0x45, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x2a, + 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, + 0x05, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x41, 0x57, 0x10, + 0x01, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x53, 0x56, 0x10, 0x02, 0x42, 0x5c, 0x0a, 0x20, 0x6f, 0x72, + 0x67, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x5a, 0x38, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, + 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1139,60 +518,30 @@ func file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDescGZIP() []byte } var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_goTypes = []interface{}{ - (ContentType)(0), // 0: kuscia.proto.api.v1alpha1.datamesh.ContentType - (*CSVWriteOptions)(nil), // 1: kuscia.proto.api.v1alpha1.datamesh.CSVWriteOptions - (*FileWriteOptions)(nil), // 2: kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions - (*CommandGetDomainDataSchema)(nil), // 3: kuscia.proto.api.v1alpha1.datamesh.CommandGetDomainDataSchema - (*CommandDomainDataQuery)(nil), // 4: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery - (*CommandDomainDataUpdate)(nil), // 5: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate - (*TicketDomainDataQuery)(nil), // 6: kuscia.proto.api.v1alpha1.datamesh.TicketDomainDataQuery - (*ActionCreateDomainDataRequest)(nil), // 7: kuscia.proto.api.v1alpha1.datamesh.ActionCreateDomainDataRequest - (*ActionCreateDomainDataResponse)(nil), // 8: kuscia.proto.api.v1alpha1.datamesh.ActionCreateDomainDataResponse - (*ActionQueryDomainDataRequest)(nil), // 9: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataRequest - (*ActionQueryDomainDataResponse)(nil), // 10: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataResponse - (*ActionUpdateDomainDataRequest)(nil), // 11: kuscia.proto.api.v1alpha1.datamesh.ActionUpdateDomainDataRequest - (*ActionUpdateDomainDataResponse)(nil), // 12: kuscia.proto.api.v1alpha1.datamesh.ActionUpdateDomainDataResponse - (*ActionDeleteDomainDataRequest)(nil), // 13: kuscia.proto.api.v1alpha1.datamesh.ActionDeleteDomainDataRequest - (*ActionDeleteDomainDataResponse)(nil), // 14: kuscia.proto.api.v1alpha1.datamesh.ActionDeleteDomainDataResponse - (*ActionQueryDomainDataSourceRequest)(nil), // 15: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataSourceRequest - (*ActionQueryDomainDataSourceResponse)(nil), // 16: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataSourceResponse - nil, // 17: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.ExtraOptionsEntry - (*CreateDomainDataRequest)(nil), // 18: kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest - (*CreateDomainDataResponse)(nil), // 19: kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataResponse - (*QueryDomainDataRequest)(nil), // 20: kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataRequest - (*QueryDomainDataResponse)(nil), // 21: kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataResponse - (*UpdateDomainDataRequest)(nil), // 22: kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataRequest - (*UpdateDomainDataResponse)(nil), // 23: kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataResponse - (*DeleteDomainDataRequest)(nil), // 24: kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataRequest - (*DeleteDomainDataResponse)(nil), // 25: kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataResponse - (*QueryDomainDataSourceRequest)(nil), // 26: kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceRequest - (*QueryDomainDataSourceResponse)(nil), // 27: kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceResponse + (ContentType)(0), // 0: kuscia.proto.api.v1alpha1.datamesh.ContentType + (*CSVWriteOptions)(nil), // 1: kuscia.proto.api.v1alpha1.datamesh.CSVWriteOptions + (*FileWriteOptions)(nil), // 2: kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions + (*CommandGetDomainDataSchema)(nil), // 3: kuscia.proto.api.v1alpha1.datamesh.CommandGetDomainDataSchema + (*CommandDomainDataQuery)(nil), // 4: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery + (*CommandDomainDataUpdate)(nil), // 5: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate + nil, // 6: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.ExtraOptionsEntry + (*CreateDomainDataRequest)(nil), // 7: kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest } var file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_depIdxs = []int32{ - 1, // 0: kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions.csv_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.CSVWriteOptions - 0, // 1: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery.content_type:type_name -> kuscia.proto.api.v1alpha1.datamesh.ContentType - 2, // 2: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery.file_write_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions - 18, // 3: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.domaindata_request:type_name -> kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest - 0, // 4: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.content_type:type_name -> kuscia.proto.api.v1alpha1.datamesh.ContentType - 2, // 5: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.file_write_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions - 17, // 6: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.extra_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.ExtraOptionsEntry - 18, // 7: kuscia.proto.api.v1alpha1.datamesh.ActionCreateDomainDataRequest.request:type_name -> kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest - 19, // 8: kuscia.proto.api.v1alpha1.datamesh.ActionCreateDomainDataResponse.response:type_name -> kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataResponse - 20, // 9: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataRequest.request:type_name -> kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataRequest - 21, // 10: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataResponse.response:type_name -> kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataResponse - 22, // 11: kuscia.proto.api.v1alpha1.datamesh.ActionUpdateDomainDataRequest.request:type_name -> kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataRequest - 23, // 12: kuscia.proto.api.v1alpha1.datamesh.ActionUpdateDomainDataResponse.response:type_name -> kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataResponse - 24, // 13: kuscia.proto.api.v1alpha1.datamesh.ActionDeleteDomainDataRequest.request:type_name -> kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataRequest - 25, // 14: kuscia.proto.api.v1alpha1.datamesh.ActionDeleteDomainDataResponse.response:type_name -> kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataResponse - 26, // 15: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataSourceRequest.request:type_name -> kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceRequest - 27, // 16: kuscia.proto.api.v1alpha1.datamesh.ActionQueryDomainDataSourceResponse.response:type_name -> kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceResponse - 17, // [17:17] is the sub-list for method output_type - 17, // [17:17] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 1, // 0: kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions.csv_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.CSVWriteOptions + 0, // 1: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery.content_type:type_name -> kuscia.proto.api.v1alpha1.datamesh.ContentType + 2, // 2: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery.file_write_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions + 7, // 3: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.domaindata_request:type_name -> kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest + 0, // 4: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.content_type:type_name -> kuscia.proto.api.v1alpha1.datamesh.ContentType + 2, // 5: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.file_write_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions + 6, // 6: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.extra_options:type_name -> kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.ExtraOptionsEntry + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_init() } @@ -1201,7 +550,6 @@ func file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_init() { return } file_kuscia_proto_api_v1alpha1_datamesh_domaindata_proto_init() - file_kuscia_proto_api_v1alpha1_datamesh_domaindatasource_proto_init() if !protoimpl.UnsafeEnabled { file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CSVWriteOptions); i { @@ -1263,138 +611,6 @@ func file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_init() { return nil } } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TicketDomainDataQuery); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionCreateDomainDataRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionCreateDomainDataResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionQueryDomainDataRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionQueryDomainDataResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionUpdateDomainDataRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionUpdateDomainDataResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionDeleteDomainDataRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionDeleteDomainDataResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionQueryDomainDataSourceRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionQueryDomainDataSourceResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_msgTypes[1].OneofWrappers = []interface{}{ (*FileWriteOptions_CsvOptions)(nil), @@ -1405,7 +621,7 @@ func file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_rawDesc, NumEnums: 1, - NumMessages: 17, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/api/v1alpha1/datamesh/flightdm.proto b/proto/api/v1alpha1/datamesh/flightdm.proto index 9fdadfb4..2b8d91f4 100644 --- a/proto/api/v1alpha1/datamesh/flightdm.proto +++ b/proto/api/v1alpha1/datamesh/flightdm.proto @@ -17,7 +17,6 @@ syntax = "proto3"; package kuscia.proto.api.v1alpha1.datamesh; import "kuscia/proto/api/v1alpha1/datamesh/domaindata.proto"; -import "kuscia/proto/api/v1alpha1/datamesh/domaindatasource.proto"; option go_package = "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh"; option java_package = "org.secretflow.v1alpha1.datamesh"; @@ -58,7 +57,7 @@ message CommandDomainDataQuery { // column name of DomainData's columns repeated string columns = 2; - // expected content type of flight data from data proxy response + // transfer content-type(expected content type of flight data from data proxy response) // for domaindata with type != table, the content_type can only be RAW, other types will not take effect ContentType content_type = 3; // if content_type is CSV, you can set file_write_options @@ -72,7 +71,7 @@ message CommandDomainDataUpdate { // create an nonexistent domaindata and get a update ticket CreateDomainDataRequest domaindata_request = 2; - // content type of flight data from client request + // transfer content-type(content type of flight data from client request) // for domaindata with type != table, the content_type can only be RAW, other types will not take effect ContentType content_type = 3; @@ -82,55 +81,3 @@ message CommandDomainDataUpdate { // extra options varies by datasource type map extra_options = 5; } - -message TicketDomainDataQuery { - // a unique identifier related to a DomainData query - string domaindata_handle = 1; -} - -// call DoAction with ActionCreateDomainDataRequest, return ActionCreateDomainDataResponse -message ActionCreateDomainDataRequest { - CreateDomainDataRequest request = 1; -} - -message ActionCreateDomainDataResponse { - CreateDomainDataResponse response = 1; -} - -// call DoAction with ActionQueryDomainDataRequest, return ActionQueryDomainDataResponse -message ActionQueryDomainDataRequest { - QueryDomainDataRequest request = 1; -} - -message ActionQueryDomainDataResponse { - QueryDomainDataResponse response = 1; -} - -// call DoAction with ActionUpdateDomainDataRequest, return ActionUpdateDomainDataResponse -message ActionUpdateDomainDataRequest { - UpdateDomainDataRequest request = 1; -} - -message ActionUpdateDomainDataResponse { - UpdateDomainDataResponse response = 1; -} - -// call DoAction with ActionDeleteDomainDataRequest, return ActionDeleteDomainDataResponse -message ActionDeleteDomainDataRequest { - DeleteDomainDataRequest request = 1; - // not supported now - bool physical_deletion = 2; -} - -message ActionDeleteDomainDataResponse { - DeleteDomainDataResponse response = 1; -} - -// call DoAction with ActionQueryDomainDataSourceRequest, return ActionQueryDomainDataSourceResponse -message ActionQueryDomainDataSourceRequest { - QueryDomainDataSourceRequest request = 1; -} - -message ActionQueryDomainDataSourceResponse { - QueryDomainDataSourceResponse response = 1; -} diff --git a/proto/api/v1alpha1/datamesh/flightinner.pb.go b/proto/api/v1alpha1/datamesh/flightinner.pb.go deleted file mode 100644 index 60a0e6c5..00000000 --- a/proto/api/v1alpha1/datamesh/flightinner.pb.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.8 -// source: kuscia/proto/api/v1alpha1/datamesh/flightinner.proto - -package datamesh - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type CommandDataMeshQuery struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Query *CommandDomainDataQuery `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` - Domaindata *DomainData `protobuf:"bytes,2,opt,name=domaindata,proto3" json:"domaindata,omitempty"` - Datasource *DomainDataSource `protobuf:"bytes,3,opt,name=datasource,proto3" json:"datasource,omitempty"` -} - -func (x *CommandDataMeshQuery) Reset() { - *x = CommandDataMeshQuery{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandDataMeshQuery) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandDataMeshQuery) ProtoMessage() {} - -func (x *CommandDataMeshQuery) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandDataMeshQuery.ProtoReflect.Descriptor instead. -func (*CommandDataMeshQuery) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescGZIP(), []int{0} -} - -func (x *CommandDataMeshQuery) GetQuery() *CommandDomainDataQuery { - if x != nil { - return x.Query - } - return nil -} - -func (x *CommandDataMeshQuery) GetDomaindata() *DomainData { - if x != nil { - return x.Domaindata - } - return nil -} - -func (x *CommandDataMeshQuery) GetDatasource() *DomainDataSource { - if x != nil { - return x.Datasource - } - return nil -} - -type CommandDataMeshUpdate struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Update *CommandDomainDataUpdate `protobuf:"bytes,1,opt,name=update,proto3" json:"update,omitempty"` - Domaindata *DomainData `protobuf:"bytes,2,opt,name=domaindata,proto3" json:"domaindata,omitempty"` - Datasource *DomainDataSource `protobuf:"bytes,3,opt,name=datasource,proto3" json:"datasource,omitempty"` -} - -func (x *CommandDataMeshUpdate) Reset() { - *x = CommandDataMeshUpdate{} - if protoimpl.UnsafeEnabled { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommandDataMeshUpdate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommandDataMeshUpdate) ProtoMessage() {} - -func (x *CommandDataMeshUpdate) ProtoReflect() protoreflect.Message { - mi := &file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommandDataMeshUpdate.ProtoReflect.Descriptor instead. -func (*CommandDataMeshUpdate) Descriptor() ([]byte, []int) { - return file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescGZIP(), []int{1} -} - -func (x *CommandDataMeshUpdate) GetUpdate() *CommandDomainDataUpdate { - if x != nil { - return x.Update - } - return nil -} - -func (x *CommandDataMeshUpdate) GetDomaindata() *DomainData { - if x != nil { - return x.Domaindata - } - return nil -} - -func (x *CommandDataMeshUpdate) GetDatasource() *DomainDataSource { - if x != nil { - return x.Datasource - } - return nil -} - -var File_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto protoreflect.FileDescriptor - -var file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDesc = []byte{ - 0x0a, 0x34, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, - 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x6e, 0x65, 0x72, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x22, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x1a, 0x33, 0x6b, 0x75, 0x73, 0x63, - 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x64, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x39, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x6d, - 0x65, 0x73, 0x68, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x31, 0x6b, 0x75, 0x73, 0x63, - 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x66, - 0x6c, 0x69, 0x67, 0x68, 0x74, 0x64, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x02, - 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, - 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x50, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x4e, 0x0a, 0x0a, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x6b, - 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x64, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6b, - 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x92, - 0x02, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, - 0x73, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x53, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, - 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x4e, 0x0a, - 0x0a, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2e, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x0a, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, - 0x0a, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x34, 0x2e, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, - 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x42, 0x5c, 0x0a, 0x20, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x64, - 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, 0x68, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x6b, - 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x65, 0x73, - 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescOnce sync.Once - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescData = file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDesc -) - -func file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescGZIP() []byte { - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescOnce.Do(func() { - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescData = protoimpl.X.CompressGZIP(file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescData) - }) - return file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDescData -} - -var file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_goTypes = []interface{}{ - (*CommandDataMeshQuery)(nil), // 0: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshQuery - (*CommandDataMeshUpdate)(nil), // 1: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshUpdate - (*CommandDomainDataQuery)(nil), // 2: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery - (*DomainData)(nil), // 3: kuscia.proto.api.v1alpha1.datamesh.DomainData - (*DomainDataSource)(nil), // 4: kuscia.proto.api.v1alpha1.datamesh.DomainDataSource - (*CommandDomainDataUpdate)(nil), // 5: kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate -} -var file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_depIdxs = []int32{ - 2, // 0: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshQuery.query:type_name -> kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataQuery - 3, // 1: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshQuery.domaindata:type_name -> kuscia.proto.api.v1alpha1.datamesh.DomainData - 4, // 2: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshQuery.datasource:type_name -> kuscia.proto.api.v1alpha1.datamesh.DomainDataSource - 5, // 3: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshUpdate.update:type_name -> kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate - 3, // 4: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshUpdate.domaindata:type_name -> kuscia.proto.api.v1alpha1.datamesh.DomainData - 4, // 5: kuscia.proto.api.v1alpha1.datamesh.CommandDataMeshUpdate.datasource:type_name -> kuscia.proto.api.v1alpha1.datamesh.DomainDataSource - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name -} - -func init() { file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_init() } -func file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_init() { - if File_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto != nil { - return - } - file_kuscia_proto_api_v1alpha1_datamesh_domaindata_proto_init() - file_kuscia_proto_api_v1alpha1_datamesh_domaindatasource_proto_init() - file_kuscia_proto_api_v1alpha1_datamesh_flightdm_proto_init() - if !protoimpl.UnsafeEnabled { - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandDataMeshQuery); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandDataMeshUpdate); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_goTypes, - DependencyIndexes: file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_depIdxs, - MessageInfos: file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_msgTypes, - }.Build() - File_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto = out.File - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_rawDesc = nil - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_goTypes = nil - file_kuscia_proto_api_v1alpha1_datamesh_flightinner_proto_depIdxs = nil -} diff --git a/proto/api/v1alpha1/datamesh/flightinner.proto b/proto/api/v1alpha1/datamesh/flightinner.proto deleted file mode 100644 index 31a26950..00000000 --- a/proto/api/v1alpha1/datamesh/flightinner.proto +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 Ant Group Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package kuscia.proto.api.v1alpha1.datamesh; - -import "kuscia/proto/api/v1alpha1/datamesh/domaindata.proto"; -import "kuscia/proto/api/v1alpha1/datamesh/domaindatasource.proto"; -import "kuscia/proto/api/v1alpha1/datamesh/flightdm.proto"; - -option go_package = "github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh"; -option java_package = "org.secretflow.v1alpha1.datamesh"; - -message CommandDataMeshQuery { - CommandDomainDataQuery query = 1; - DomainData domaindata = 2 ; - DomainDataSource datasource = 3; -} - -message CommandDataMeshUpdate { - CommandDomainDataUpdate update = 1; - DomainData domaindata = 2 ; - DomainDataSource datasource = 3; -} diff --git a/proto/api/v1alpha1/errorcode/error_code.pb.go b/proto/api/v1alpha1/errorcode/error_code.pb.go new file mode 100644 index 00000000..e80973b1 --- /dev/null +++ b/proto/api/v1alpha1/errorcode/error_code.pb.go @@ -0,0 +1,614 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.8 +// source: kuscia/proto/api/v1alpha1/errorcode/error_code.proto + +package errorcode + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ErrorCode int32 + +const ( + ErrorCode_SUCCESS ErrorCode = 0 + // Kuscia API + ErrorCode_KusciaAPIErrRequestValidate ErrorCode = 11100 + ErrorCode_KusciaAPIErrForUnexpected ErrorCode = 11101 + ErrorCode_KusciaAPIErrAuthFailed ErrorCode = 11102 + ErrorCode_KusciaAPIErrRequestMasterFailed ErrorCode = 11103 + ErrorCode_KusciaAPIErrLiteAPINotSupport ErrorCode = 11104 + ErrorCode_KusciaAPIErrCreateJob ErrorCode = 11201 + ErrorCode_KusciaAPIErrQueryJob ErrorCode = 11202 + ErrorCode_KusciaAPIErrQueryJobStatus ErrorCode = 11203 + ErrorCode_KusciaAPIErrDeleteJob ErrorCode = 11204 + ErrorCode_KusciaAPIErrStopJob ErrorCode = 11205 + ErrorCode_KusciaAPIErrApproveJob ErrorCode = 11206 + ErrorCode_KusciaAPIErrSuspendJob ErrorCode = 11207 + ErrorCode_KusciaAPIErrRestartJob ErrorCode = 11208 + ErrorCode_KusciaAPIErrCancelJob ErrorCode = 11209 + ErrorCode_KusciaAPIErrSuspendNotRunningJob ErrorCode = 11210 + ErrorCode_KusciaAPIErrRestartNotSuspendedOrFailedJob ErrorCode = 11211 + ErrorCode_KusciaAPIErrCreateDomain ErrorCode = 11300 + ErrorCode_KusciaAPIErrQueryDomain ErrorCode = 11301 + ErrorCode_KusciaAPIErrQueryDomainStatus ErrorCode = 11302 + ErrorCode_KusciaAPIErrUpdateDomain ErrorCode = 11303 + ErrorCode_KusciaAPIErrDeleteDomain ErrorCode = 11304 + ErrorCode_KusciaAPIErrDomainNotExists ErrorCode = 11305 + ErrorCode_KusciaAPIErrDomainExists ErrorCode = 11306 + ErrorCode_KusciaAPIErrCreateDomainRoute ErrorCode = 11400 + ErrorCode_KusciaAPIErrQueryDomainRoute ErrorCode = 11401 + ErrorCode_KusciaAPIErrQueryDomainRouteStatus ErrorCode = 11402 + ErrorCode_KusciaAPIErrDeleteDomainRoute ErrorCode = 11403 + ErrorCode_KusciaAPIErrDomainRouteNotExists ErrorCode = 11404 + ErrorCode_KusciaAPIErrDomainRouteExists ErrorCode = 11405 + ErrorCode_KusciaAPIErrCreateDomainDataFailed ErrorCode = 11500 + ErrorCode_KusciaAPIErrDeleteDomainDataFailed ErrorCode = 11501 + ErrorCode_KusciaAPIErrGetDomainDataFailed ErrorCode = 11502 + ErrorCode_KusciaAPIErrListDomainDataFailed ErrorCode = 11503 + ErrorCode_KusciaAPIErrMergeDomainDataFailed ErrorCode = 11504 + ErrorCode_KusciaAPIErrPatchDomainDataFailed ErrorCode = 11505 + ErrorCode_KusciaAPIErrDomainDataNotExists ErrorCode = 11506 + ErrorCode_KusciaAPIErrDomainDataExists ErrorCode = 11507 + ErrorCode_KusciaAPIErrCreateServing ErrorCode = 11600 + ErrorCode_KusciaAPIErrQueryServing ErrorCode = 11601 + ErrorCode_KusciaAPIErrQueryServingStatus ErrorCode = 11602 + ErrorCode_KusciaAPIErrUpdateServing ErrorCode = 11603 + ErrorCode_KusciaAPIErrDeleteServing ErrorCode = 11604 + ErrorCode_KusciaAPIErrCreateDomainDataGrant ErrorCode = 11700 + ErrorCode_KusciaAPIErrUpdateDomainDataGrant ErrorCode = 11701 + ErrorCode_KusciaAPIErrQueryDomainDataGrant ErrorCode = 11702 + ErrorCode_KusciaAPIErrDeleteDomainDataGrant ErrorCode = 11703 + ErrorCode_KusciaAPIErrDomainDataGrantExists ErrorCode = 11704 + ErrorCode_KusciaAPIErrDomainDataGrantNotExists ErrorCode = 11705 + ErrorCode_KusciaAPIErrCreateDomainDataSource ErrorCode = 11800 + ErrorCode_KusciaAPIErrUpdateDomainDataSource ErrorCode = 11801 + ErrorCode_KusciaAPIErrQueryDomainDataSource ErrorCode = 11802 + ErrorCode_KusciaAPIErrBatchQueryDomainDataSource ErrorCode = 11803 + ErrorCode_KusciaAPIErrDeleteDomainDataSource ErrorCode = 11804 + ErrorCode_KusciaAPIErrDomainDataSourceExists ErrorCode = 11805 + ErrorCode_KusciaAPIErrDomainDataSourceNotExists ErrorCode = 11806 + ErrorCode_KusciaAPIErrDomainDataSourceInfoEncodeFailed ErrorCode = 11807 + ErrorCode_KusciaAPIErrListDomainDataSource ErrorCode = 11808 + // data mesh + ErrorCode_DataMeshErrRequestInvalidate ErrorCode = 12100 + ErrorCode_DataMeshErrForUnexpected ErrorCode = 12101 + ErrorCode_DataMeshErrCreateDomainData ErrorCode = 12200 + ErrorCode_DataMeshErrQueryDomainData ErrorCode = 12201 + ErrorCode_DataMeshErrGetDomainDataFromKubeFailed ErrorCode = 12202 + ErrorCode_DataMeshErrMergeDomainDataFailed ErrorCode = 12203 + ErrorCode_DataMeshErrPatchDomainDataFailed ErrorCode = 12204 + ErrorCode_DataMeshErrDeleteDomainDataFailed ErrorCode = 12205 + ErrorCode_DataMeshErrCreateDomainDataSource ErrorCode = 12300 + ErrorCode_DataMeshErrParseDomainDataSourceFailed ErrorCode = 12301 + ErrorCode_DataMeshErrQueryDomainDataSource ErrorCode = 12302 + ErrorCode_DataMeshErrDeleteDomainDataSourceFailed ErrorCode = 12303 + ErrorCode_DataMeshErrDomainDataSourceExists ErrorCode = 12304 + ErrorCode_DataMeshErrDomainDataSourceNotExists ErrorCode = 12305 + ErrorCode_DataMeshErrGetDomainDataSourceFromKubeFailed ErrorCode = 12306 + ErrorCode_DataMeshErrDecodeDomainDataSourceInfoFailed ErrorCode = 12307 + ErrorCode_DataMeshErrEncodeDomainDataSourceInfoFailed ErrorCode = 12308 + ErrorCode_DataMeshErrMergeDomainDataSourceFailed ErrorCode = 12309 + ErrorCode_DataMeshErrPatchDomainDataSourceFailed ErrorCode = 12310 + ErrorCode_DataMeshErrCreateDomainDataGrant ErrorCode = 12400 + ErrorCode_DataMeshErrUpdateDomainDataGrant ErrorCode = 12401 + ErrorCode_DataMeshErrQueryDomainDataGrant ErrorCode = 12402 + ErrorCode_DataMeshErrDeleteDomainDataGrant ErrorCode = 12403 + ErrorCode_DataMeshErrDomainDataGrantExists ErrorCode = 12404 + ErrorCode_DataMeshErrDomainDataGrantNotExists ErrorCode = 12405 + // conf manager + ErrorCode_ConfManagerErrRequestInvalidate ErrorCode = 2000 + ErrorCode_ConfManagerErrForUnexpected ErrorCode = 2001 + ErrorCode_ConfManagerErrCreateConfiguration ErrorCode = 2102 + ErrorCode_ConfManagerErrQueryConfiguration ErrorCode = 2103 + ErrorCode_ConfManagerErrGenerateKeyCerts ErrorCode = 2200 +) + +// Enum value maps for ErrorCode. +var ( + ErrorCode_name = map[int32]string{ + 0: "SUCCESS", + 11100: "KusciaAPIErrRequestValidate", + 11101: "KusciaAPIErrForUnexpected", + 11102: "KusciaAPIErrAuthFailed", + 11103: "KusciaAPIErrRequestMasterFailed", + 11104: "KusciaAPIErrLiteAPINotSupport", + 11201: "KusciaAPIErrCreateJob", + 11202: "KusciaAPIErrQueryJob", + 11203: "KusciaAPIErrQueryJobStatus", + 11204: "KusciaAPIErrDeleteJob", + 11205: "KusciaAPIErrStopJob", + 11206: "KusciaAPIErrApproveJob", + 11207: "KusciaAPIErrSuspendJob", + 11208: "KusciaAPIErrRestartJob", + 11209: "KusciaAPIErrCancelJob", + 11210: "KusciaAPIErrSuspendNotRunningJob", + 11211: "KusciaAPIErrRestartNotSuspendedOrFailedJob", + 11300: "KusciaAPIErrCreateDomain", + 11301: "KusciaAPIErrQueryDomain", + 11302: "KusciaAPIErrQueryDomainStatus", + 11303: "KusciaAPIErrUpdateDomain", + 11304: "KusciaAPIErrDeleteDomain", + 11305: "KusciaAPIErrDomainNotExists", + 11306: "KusciaAPIErrDomainExists", + 11400: "KusciaAPIErrCreateDomainRoute", + 11401: "KusciaAPIErrQueryDomainRoute", + 11402: "KusciaAPIErrQueryDomainRouteStatus", + 11403: "KusciaAPIErrDeleteDomainRoute", + 11404: "KusciaAPIErrDomainRouteNotExists", + 11405: "KusciaAPIErrDomainRouteExists", + 11500: "KusciaAPIErrCreateDomainDataFailed", + 11501: "KusciaAPIErrDeleteDomainDataFailed", + 11502: "KusciaAPIErrGetDomainDataFailed", + 11503: "KusciaAPIErrListDomainDataFailed", + 11504: "KusciaAPIErrMergeDomainDataFailed", + 11505: "KusciaAPIErrPatchDomainDataFailed", + 11506: "KusciaAPIErrDomainDataNotExists", + 11507: "KusciaAPIErrDomainDataExists", + 11600: "KusciaAPIErrCreateServing", + 11601: "KusciaAPIErrQueryServing", + 11602: "KusciaAPIErrQueryServingStatus", + 11603: "KusciaAPIErrUpdateServing", + 11604: "KusciaAPIErrDeleteServing", + 11700: "KusciaAPIErrCreateDomainDataGrant", + 11701: "KusciaAPIErrUpdateDomainDataGrant", + 11702: "KusciaAPIErrQueryDomainDataGrant", + 11703: "KusciaAPIErrDeleteDomainDataGrant", + 11704: "KusciaAPIErrDomainDataGrantExists", + 11705: "KusciaAPIErrDomainDataGrantNotExists", + 11800: "KusciaAPIErrCreateDomainDataSource", + 11801: "KusciaAPIErrUpdateDomainDataSource", + 11802: "KusciaAPIErrQueryDomainDataSource", + 11803: "KusciaAPIErrBatchQueryDomainDataSource", + 11804: "KusciaAPIErrDeleteDomainDataSource", + 11805: "KusciaAPIErrDomainDataSourceExists", + 11806: "KusciaAPIErrDomainDataSourceNotExists", + 11807: "KusciaAPIErrDomainDataSourceInfoEncodeFailed", + 11808: "KusciaAPIErrListDomainDataSource", + 12100: "DataMeshErrRequestInvalidate", + 12101: "DataMeshErrForUnexpected", + 12200: "DataMeshErrCreateDomainData", + 12201: "DataMeshErrQueryDomainData", + 12202: "DataMeshErrGetDomainDataFromKubeFailed", + 12203: "DataMeshErrMergeDomainDataFailed", + 12204: "DataMeshErrPatchDomainDataFailed", + 12205: "DataMeshErrDeleteDomainDataFailed", + 12300: "DataMeshErrCreateDomainDataSource", + 12301: "DataMeshErrParseDomainDataSourceFailed", + 12302: "DataMeshErrQueryDomainDataSource", + 12303: "DataMeshErrDeleteDomainDataSourceFailed", + 12304: "DataMeshErrDomainDataSourceExists", + 12305: "DataMeshErrDomainDataSourceNotExists", + 12306: "DataMeshErrGetDomainDataSourceFromKubeFailed", + 12307: "DataMeshErrDecodeDomainDataSourceInfoFailed", + 12308: "DataMeshErrEncodeDomainDataSourceInfoFailed", + 12309: "DataMeshErrMergeDomainDataSourceFailed", + 12310: "DataMeshErrPatchDomainDataSourceFailed", + 12400: "DataMeshErrCreateDomainDataGrant", + 12401: "DataMeshErrUpdateDomainDataGrant", + 12402: "DataMeshErrQueryDomainDataGrant", + 12403: "DataMeshErrDeleteDomainDataGrant", + 12404: "DataMeshErrDomainDataGrantExists", + 12405: "DataMeshErrDomainDataGrantNotExists", + 2000: "ConfManagerErrRequestInvalidate", + 2001: "ConfManagerErrForUnexpected", + 2102: "ConfManagerErrCreateConfiguration", + 2103: "ConfManagerErrQueryConfiguration", + 2200: "ConfManagerErrGenerateKeyCerts", + } + ErrorCode_value = map[string]int32{ + "SUCCESS": 0, + "KusciaAPIErrRequestValidate": 11100, + "KusciaAPIErrForUnexpected": 11101, + "KusciaAPIErrAuthFailed": 11102, + "KusciaAPIErrRequestMasterFailed": 11103, + "KusciaAPIErrLiteAPINotSupport": 11104, + "KusciaAPIErrCreateJob": 11201, + "KusciaAPIErrQueryJob": 11202, + "KusciaAPIErrQueryJobStatus": 11203, + "KusciaAPIErrDeleteJob": 11204, + "KusciaAPIErrStopJob": 11205, + "KusciaAPIErrApproveJob": 11206, + "KusciaAPIErrSuspendJob": 11207, + "KusciaAPIErrRestartJob": 11208, + "KusciaAPIErrCancelJob": 11209, + "KusciaAPIErrSuspendNotRunningJob": 11210, + "KusciaAPIErrRestartNotSuspendedOrFailedJob": 11211, + "KusciaAPIErrCreateDomain": 11300, + "KusciaAPIErrQueryDomain": 11301, + "KusciaAPIErrQueryDomainStatus": 11302, + "KusciaAPIErrUpdateDomain": 11303, + "KusciaAPIErrDeleteDomain": 11304, + "KusciaAPIErrDomainNotExists": 11305, + "KusciaAPIErrDomainExists": 11306, + "KusciaAPIErrCreateDomainRoute": 11400, + "KusciaAPIErrQueryDomainRoute": 11401, + "KusciaAPIErrQueryDomainRouteStatus": 11402, + "KusciaAPIErrDeleteDomainRoute": 11403, + "KusciaAPIErrDomainRouteNotExists": 11404, + "KusciaAPIErrDomainRouteExists": 11405, + "KusciaAPIErrCreateDomainDataFailed": 11500, + "KusciaAPIErrDeleteDomainDataFailed": 11501, + "KusciaAPIErrGetDomainDataFailed": 11502, + "KusciaAPIErrListDomainDataFailed": 11503, + "KusciaAPIErrMergeDomainDataFailed": 11504, + "KusciaAPIErrPatchDomainDataFailed": 11505, + "KusciaAPIErrDomainDataNotExists": 11506, + "KusciaAPIErrDomainDataExists": 11507, + "KusciaAPIErrCreateServing": 11600, + "KusciaAPIErrQueryServing": 11601, + "KusciaAPIErrQueryServingStatus": 11602, + "KusciaAPIErrUpdateServing": 11603, + "KusciaAPIErrDeleteServing": 11604, + "KusciaAPIErrCreateDomainDataGrant": 11700, + "KusciaAPIErrUpdateDomainDataGrant": 11701, + "KusciaAPIErrQueryDomainDataGrant": 11702, + "KusciaAPIErrDeleteDomainDataGrant": 11703, + "KusciaAPIErrDomainDataGrantExists": 11704, + "KusciaAPIErrDomainDataGrantNotExists": 11705, + "KusciaAPIErrCreateDomainDataSource": 11800, + "KusciaAPIErrUpdateDomainDataSource": 11801, + "KusciaAPIErrQueryDomainDataSource": 11802, + "KusciaAPIErrBatchQueryDomainDataSource": 11803, + "KusciaAPIErrDeleteDomainDataSource": 11804, + "KusciaAPIErrDomainDataSourceExists": 11805, + "KusciaAPIErrDomainDataSourceNotExists": 11806, + "KusciaAPIErrDomainDataSourceInfoEncodeFailed": 11807, + "KusciaAPIErrListDomainDataSource": 11808, + "DataMeshErrRequestInvalidate": 12100, + "DataMeshErrForUnexpected": 12101, + "DataMeshErrCreateDomainData": 12200, + "DataMeshErrQueryDomainData": 12201, + "DataMeshErrGetDomainDataFromKubeFailed": 12202, + "DataMeshErrMergeDomainDataFailed": 12203, + "DataMeshErrPatchDomainDataFailed": 12204, + "DataMeshErrDeleteDomainDataFailed": 12205, + "DataMeshErrCreateDomainDataSource": 12300, + "DataMeshErrParseDomainDataSourceFailed": 12301, + "DataMeshErrQueryDomainDataSource": 12302, + "DataMeshErrDeleteDomainDataSourceFailed": 12303, + "DataMeshErrDomainDataSourceExists": 12304, + "DataMeshErrDomainDataSourceNotExists": 12305, + "DataMeshErrGetDomainDataSourceFromKubeFailed": 12306, + "DataMeshErrDecodeDomainDataSourceInfoFailed": 12307, + "DataMeshErrEncodeDomainDataSourceInfoFailed": 12308, + "DataMeshErrMergeDomainDataSourceFailed": 12309, + "DataMeshErrPatchDomainDataSourceFailed": 12310, + "DataMeshErrCreateDomainDataGrant": 12400, + "DataMeshErrUpdateDomainDataGrant": 12401, + "DataMeshErrQueryDomainDataGrant": 12402, + "DataMeshErrDeleteDomainDataGrant": 12403, + "DataMeshErrDomainDataGrantExists": 12404, + "DataMeshErrDomainDataGrantNotExists": 12405, + "ConfManagerErrRequestInvalidate": 2000, + "ConfManagerErrForUnexpected": 2001, + "ConfManagerErrCreateConfiguration": 2102, + "ConfManagerErrQueryConfiguration": 2103, + "ConfManagerErrGenerateKeyCerts": 2200, + } +) + +func (x ErrorCode) Enum() *ErrorCode { + p := new(ErrorCode) + *p = x + return p +} + +func (x ErrorCode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ErrorCode) Descriptor() protoreflect.EnumDescriptor { + return file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_enumTypes[0].Descriptor() +} + +func (ErrorCode) Type() protoreflect.EnumType { + return &file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_enumTypes[0] +} + +func (x ErrorCode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ErrorCode.Descriptor instead. +func (ErrorCode) EnumDescriptor() ([]byte, []int) { + return file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescGZIP(), []int{0} +} + +var File_kuscia_proto_api_v1alpha1_errorcode_error_code_proto protoreflect.FileDescriptor + +var file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDesc = []byte{ + 0x0a, 0x34, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x63, 0x6f, 0x64, 0x65, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2a, 0xe4, 0x19, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, + 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1b, + 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x10, 0xdc, 0x56, 0x12, 0x1e, + 0x0a, 0x19, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x46, 0x6f, + 0x72, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0xdd, 0x56, 0x12, 0x1b, + 0x0a, 0x16, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x41, 0x75, + 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0xde, 0x56, 0x12, 0x24, 0x0a, 0x1f, 0x4b, + 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0xdf, + 0x56, 0x12, 0x22, 0x0a, 0x1d, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, + 0x72, 0x4c, 0x69, 0x74, 0x65, 0x41, 0x50, 0x49, 0x4e, 0x6f, 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x10, 0xe0, 0x56, 0x12, 0x1a, 0x0a, 0x15, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, + 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x10, 0xc1, + 0x57, 0x12, 0x19, 0x0a, 0x14, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, + 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x10, 0xc2, 0x57, 0x12, 0x1f, 0x0a, 0x1a, + 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x10, 0xc3, 0x57, 0x12, 0x1a, 0x0a, + 0x15, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x10, 0xc4, 0x57, 0x12, 0x18, 0x0a, 0x13, 0x4b, 0x75, 0x73, + 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x53, 0x74, 0x6f, 0x70, 0x4a, 0x6f, 0x62, + 0x10, 0xc5, 0x57, 0x12, 0x1b, 0x0a, 0x16, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, + 0x45, 0x72, 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x10, 0xc6, 0x57, + 0x12, 0x1b, 0x0a, 0x16, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, + 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x4a, 0x6f, 0x62, 0x10, 0xc7, 0x57, 0x12, 0x1b, 0x0a, + 0x16, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x52, 0x65, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x10, 0xc8, 0x57, 0x12, 0x1a, 0x0a, 0x15, 0x4b, 0x75, + 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x4a, 0x6f, 0x62, 0x10, 0xc9, 0x57, 0x12, 0x25, 0x0a, 0x20, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, + 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, + 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x62, 0x10, 0xca, 0x57, 0x12, 0x2f, 0x0a, + 0x2a, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x52, 0x65, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x4f, 0x72, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x10, 0xcb, 0x57, 0x12, 0x1d, + 0x0a, 0x18, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0xa4, 0x58, 0x12, 0x1c, 0x0a, + 0x17, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0xa5, 0x58, 0x12, 0x22, 0x0a, 0x1d, 0x4b, + 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x10, 0xa6, 0x58, 0x12, + 0x1d, 0x0a, 0x18, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0xa7, 0x58, 0x12, 0x1d, + 0x0a, 0x18, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0xa8, 0x58, 0x12, 0x20, 0x0a, + 0x1b, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0xa9, 0x58, 0x12, + 0x1d, 0x0a, 0x18, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0xaa, 0x58, 0x12, 0x22, + 0x0a, 0x1d, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x10, + 0x88, 0x59, 0x12, 0x21, 0x0a, 0x1c, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, + 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x10, 0x89, 0x59, 0x12, 0x27, 0x0a, 0x22, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, + 0x50, 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x10, 0x8a, 0x59, 0x12, 0x22, + 0x0a, 0x1d, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x10, + 0x8b, 0x59, 0x12, 0x25, 0x0a, 0x20, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, + 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x4e, 0x6f, 0x74, + 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0x8c, 0x59, 0x12, 0x22, 0x0a, 0x1d, 0x4b, 0x75, 0x73, + 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0x8d, 0x59, 0x12, 0x27, 0x0a, + 0x22, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x10, 0xec, 0x59, 0x12, 0x27, 0x0a, 0x22, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, + 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0xed, 0x59, 0x12, + 0x24, 0x0a, 0x1f, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x47, + 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x10, 0xee, 0x59, 0x12, 0x25, 0x0a, 0x20, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, + 0x50, 0x49, 0x45, 0x72, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, + 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0xef, 0x59, 0x12, 0x26, 0x0a, 0x21, + 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x4d, 0x65, 0x72, 0x67, + 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x10, 0xf0, 0x59, 0x12, 0x26, 0x0a, 0x21, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, + 0x49, 0x45, 0x72, 0x72, 0x50, 0x61, 0x74, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, + 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0xf1, 0x59, 0x12, 0x24, 0x0a, 0x1f, + 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, + 0xf2, 0x59, 0x12, 0x21, 0x0a, 0x1c, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, + 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x78, 0x69, 0x73, + 0x74, 0x73, 0x10, 0xf3, 0x59, 0x12, 0x1e, 0x0a, 0x19, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, + 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x6e, 0x67, 0x10, 0xd0, 0x5a, 0x12, 0x1d, 0x0a, 0x18, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, + 0x50, 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, + 0x67, 0x10, 0xd1, 0x5a, 0x12, 0x23, 0x0a, 0x1e, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, + 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x10, 0xd2, 0x5a, 0x12, 0x1e, 0x0a, 0x19, 0x4b, 0x75, 0x73, + 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x10, 0xd3, 0x5a, 0x12, 0x1e, 0x0a, 0x19, 0x4b, 0x75, 0x73, + 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x10, 0xd4, 0x5a, 0x12, 0x26, 0x0a, 0x21, 0x4b, 0x75, 0x73, + 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x10, 0xb4, + 0x5b, 0x12, 0x26, 0x0a, 0x21, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, + 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x10, 0xb5, 0x5b, 0x12, 0x25, 0x0a, 0x20, 0x4b, 0x75, 0x73, + 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x10, 0xb6, 0x5b, + 0x12, 0x26, 0x0a, 0x21, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, + 0x47, 0x72, 0x61, 0x6e, 0x74, 0x10, 0xb7, 0x5b, 0x12, 0x26, 0x0a, 0x21, 0x4b, 0x75, 0x73, 0x63, + 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, + 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0xb8, 0x5b, + 0x12, 0x29, 0x0a, 0x24, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x4e, + 0x6f, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0xb9, 0x5b, 0x12, 0x27, 0x0a, 0x22, 0x4b, + 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x10, 0x98, 0x5c, 0x12, 0x27, 0x0a, 0x22, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, + 0x49, 0x45, 0x72, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, 0x99, 0x5c, 0x12, 0x26, 0x0a, + 0x21, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x10, 0x9a, 0x5c, 0x12, 0x2b, 0x0a, 0x26, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, + 0x50, 0x49, 0x45, 0x72, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, + 0x9b, 0x5c, 0x12, 0x27, 0x0a, 0x22, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, + 0x72, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, 0x9c, 0x5c, 0x12, 0x27, 0x0a, 0x22, 0x4b, + 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, + 0x73, 0x10, 0x9d, 0x5c, 0x12, 0x2a, 0x0a, 0x25, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, + 0x49, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0x9e, 0x5c, + 0x12, 0x31, 0x0a, 0x2c, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, 0x45, 0x72, 0x72, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x10, 0x9f, 0x5c, 0x12, 0x25, 0x0a, 0x20, 0x4b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x41, 0x50, 0x49, + 0x45, 0x72, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, 0xa0, 0x5c, 0x12, 0x21, 0x0a, 0x1c, 0x44, 0x61, + 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x10, 0xc4, 0x5e, 0x12, 0x1d, 0x0a, + 0x18, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x46, 0x6f, 0x72, 0x55, + 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0xc5, 0x5e, 0x12, 0x20, 0x0a, 0x1b, + 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x10, 0xa8, 0x5f, 0x12, 0x1f, + 0x0a, 0x1a, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x10, 0xa9, 0x5f, 0x12, + 0x2b, 0x0a, 0x26, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x47, 0x65, + 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x4b, + 0x75, 0x62, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0xaa, 0x5f, 0x12, 0x25, 0x0a, 0x20, + 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x10, 0xab, 0x5f, 0x12, 0x25, 0x0a, 0x20, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, + 0x72, 0x72, 0x50, 0x61, 0x74, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0xac, 0x5f, 0x12, 0x26, 0x0a, 0x21, 0x44, 0x61, + 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, + 0xad, 0x5f, 0x12, 0x26, 0x0a, 0x21, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, + 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, 0x8c, 0x60, 0x12, 0x2b, 0x0a, 0x26, 0x44, 0x61, + 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x50, 0x61, 0x72, 0x73, 0x65, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x10, 0x8d, 0x60, 0x12, 0x25, 0x0a, 0x20, 0x44, 0x61, 0x74, 0x61, 0x4d, + 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, 0x8e, 0x60, 0x12, 0x2c, + 0x0a, 0x27, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x8f, 0x60, 0x12, 0x26, 0x0a, 0x21, + 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, + 0x73, 0x10, 0x90, 0x60, 0x12, 0x29, 0x0a, 0x24, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, + 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0x91, 0x60, 0x12, + 0x31, 0x0a, 0x2c, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x47, 0x65, + 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x4b, 0x75, 0x62, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, + 0x92, 0x60, 0x12, 0x30, 0x0a, 0x2b, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, + 0x72, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x10, 0x93, 0x60, 0x12, 0x30, 0x0a, 0x2b, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, + 0x45, 0x72, 0x72, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, + 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x46, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x10, 0x94, 0x60, 0x12, 0x2b, 0x0a, 0x26, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, + 0x73, 0x68, 0x45, 0x72, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x10, 0x95, 0x60, 0x12, 0x2b, 0x0a, 0x26, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, + 0x72, 0x72, 0x50, 0x61, 0x74, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x96, 0x60, + 0x12, 0x25, 0x0a, 0x20, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x47, + 0x72, 0x61, 0x6e, 0x74, 0x10, 0xf0, 0x60, 0x12, 0x25, 0x0a, 0x20, 0x44, 0x61, 0x74, 0x61, 0x4d, + 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x10, 0xf1, 0x60, 0x12, 0x24, + 0x0a, 0x1f, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, + 0x74, 0x10, 0xf2, 0x60, 0x12, 0x25, 0x0a, 0x20, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, + 0x45, 0x72, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, + 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x10, 0xf3, 0x60, 0x12, 0x25, 0x0a, 0x20, 0x44, + 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x44, 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, + 0xf4, 0x60, 0x12, 0x28, 0x0a, 0x23, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x68, 0x45, 0x72, + 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x47, 0x72, 0x61, 0x6e, 0x74, + 0x4e, 0x6f, 0x74, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x10, 0xf5, 0x60, 0x12, 0x24, 0x0a, 0x1f, + 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x45, 0x72, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x10, + 0xd0, 0x0f, 0x12, 0x20, 0x0a, 0x1b, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x72, 0x45, 0x72, 0x72, 0x46, 0x6f, 0x72, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x10, 0xd1, 0x0f, 0x12, 0x26, 0x0a, 0x21, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x45, 0x72, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0xb6, 0x10, 0x12, 0x25, 0x0a, 0x20, + 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x45, 0x72, 0x72, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x10, 0xb7, 0x10, 0x12, 0x23, 0x0a, 0x1e, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x72, 0x45, 0x72, 0x72, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x43, 0x65, 0x72, 0x74, 0x73, 0x10, 0x98, 0x11, 0x42, 0x5e, 0x0a, 0x21, 0x6f, 0x72, 0x67, 0x2e, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x5a, 0x39, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x66, 0x6c, 0x6f, 0x77, 0x2f, 0x6b, 0x75, 0x73, 0x63, 0x69, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescOnce sync.Once + file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescData = file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDesc +) + +func file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescGZIP() []byte { + file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescOnce.Do(func() { + file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescData = protoimpl.X.CompressGZIP(file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescData) + }) + return file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDescData +} + +var file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_goTypes = []interface{}{ + (ErrorCode)(0), // 0: kuscia.proto.api.v1alpha1.ErrorCode +} +var file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_init() } +func file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_init() { + if File_kuscia_proto_api_v1alpha1_errorcode_error_code_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_goTypes, + DependencyIndexes: file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_depIdxs, + EnumInfos: file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_enumTypes, + }.Build() + File_kuscia_proto_api_v1alpha1_errorcode_error_code_proto = out.File + file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_rawDesc = nil + file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_goTypes = nil + file_kuscia_proto_api_v1alpha1_errorcode_error_code_proto_depIdxs = nil +} diff --git a/proto/api/v1alpha1/errorcode/error_code.proto b/proto/api/v1alpha1/errorcode/error_code.proto new file mode 100644 index 00000000..85f3b39e --- /dev/null +++ b/proto/api/v1alpha1/errorcode/error_code.proto @@ -0,0 +1,129 @@ +// Copyright 2024 Ant Group Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package kuscia.proto.api.v1alpha1; + +option go_package = "github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode"; +option java_package = "org.secretflow.v1alpha1.errorcode"; + +enum ErrorCode { + + SUCCESS = 0; + + // Kuscia API + KusciaAPIErrRequestValidate = 11100; + KusciaAPIErrForUnexpected = 11101; + KusciaAPIErrAuthFailed = 11102; + KusciaAPIErrRequestMasterFailed = 11103; + KusciaAPIErrLiteAPINotSupport = 11104; + + KusciaAPIErrCreateJob = 11201; + KusciaAPIErrQueryJob = 11202; + KusciaAPIErrQueryJobStatus = 11203; + KusciaAPIErrDeleteJob = 11204; + KusciaAPIErrStopJob = 11205; + KusciaAPIErrApproveJob = 11206; + KusciaAPIErrSuspendJob = 11207; + KusciaAPIErrRestartJob = 11208; + KusciaAPIErrCancelJob = 11209; + KusciaAPIErrSuspendNotRunningJob = 11210; + KusciaAPIErrRestartNotSuspendedOrFailedJob = 11211; + + KusciaAPIErrCreateDomain = 11300; + KusciaAPIErrQueryDomain = 11301; + KusciaAPIErrQueryDomainStatus = 11302; + KusciaAPIErrUpdateDomain = 11303; + KusciaAPIErrDeleteDomain = 11304; + KusciaAPIErrDomainNotExists = 11305; + KusciaAPIErrDomainExists = 11306; + + KusciaAPIErrCreateDomainRoute = 11400; + KusciaAPIErrQueryDomainRoute = 11401; + KusciaAPIErrQueryDomainRouteStatus = 11402; + KusciaAPIErrDeleteDomainRoute = 11403; + KusciaAPIErrDomainRouteNotExists = 11404; + KusciaAPIErrDomainRouteExists = 11405; + + KusciaAPIErrCreateDomainDataFailed = 11500; + KusciaAPIErrDeleteDomainDataFailed = 11501; + KusciaAPIErrGetDomainDataFailed = 11502; + KusciaAPIErrListDomainDataFailed = 11503; + KusciaAPIErrMergeDomainDataFailed = 11504; + KusciaAPIErrPatchDomainDataFailed = 11505; + KusciaAPIErrDomainDataNotExists = 11506; + KusciaAPIErrDomainDataExists = 11507; + + KusciaAPIErrCreateServing = 11600; + KusciaAPIErrQueryServing = 11601; + KusciaAPIErrQueryServingStatus = 11602; + KusciaAPIErrUpdateServing = 11603; + KusciaAPIErrDeleteServing = 11604; + + KusciaAPIErrCreateDomainDataGrant = 11700; + KusciaAPIErrUpdateDomainDataGrant = 11701; + KusciaAPIErrQueryDomainDataGrant = 11702; + KusciaAPIErrDeleteDomainDataGrant = 11703; + KusciaAPIErrDomainDataGrantExists = 11704; + KusciaAPIErrDomainDataGrantNotExists = 11705; + + KusciaAPIErrCreateDomainDataSource = 11800; + KusciaAPIErrUpdateDomainDataSource = 11801; + KusciaAPIErrQueryDomainDataSource = 11802; + KusciaAPIErrBatchQueryDomainDataSource = 11803; + KusciaAPIErrDeleteDomainDataSource = 11804; + KusciaAPIErrDomainDataSourceExists = 11805; + KusciaAPIErrDomainDataSourceNotExists = 11806; + KusciaAPIErrDomainDataSourceInfoEncodeFailed = 11807; + KusciaAPIErrListDomainDataSource = 11808; + + // data mesh + DataMeshErrRequestInvalidate = 12100; + DataMeshErrForUnexpected = 12101; + + DataMeshErrCreateDomainData = 12200; + DataMeshErrQueryDomainData = 12201; + DataMeshErrGetDomainDataFromKubeFailed = 12202; + DataMeshErrMergeDomainDataFailed = 12203; + DataMeshErrPatchDomainDataFailed = 12204; + DataMeshErrDeleteDomainDataFailed = 12205; + + DataMeshErrCreateDomainDataSource = 12300; + DataMeshErrParseDomainDataSourceFailed = 12301; + DataMeshErrQueryDomainDataSource = 12302; + DataMeshErrDeleteDomainDataSourceFailed = 12303; + DataMeshErrDomainDataSourceExists = 12304; + DataMeshErrDomainDataSourceNotExists = 12305; + DataMeshErrGetDomainDataSourceFromKubeFailed = 12306; + DataMeshErrDecodeDomainDataSourceInfoFailed = 12307; + DataMeshErrEncodeDomainDataSourceInfoFailed = 12308; + DataMeshErrMergeDomainDataSourceFailed = 12309; + DataMeshErrPatchDomainDataSourceFailed = 12310; + + DataMeshErrCreateDomainDataGrant = 12400; + DataMeshErrUpdateDomainDataGrant = 12401; + DataMeshErrQueryDomainDataGrant = 12402; + DataMeshErrDeleteDomainDataGrant = 12403; + DataMeshErrDomainDataGrantExists = 12404; + DataMeshErrDomainDataGrantNotExists = 12405; + + // conf manager + ConfManagerErrRequestInvalidate = 2000; + ConfManagerErrForUnexpected = 2001; + ConfManagerErrCreateConfiguration = 2102; + ConfManagerErrQueryConfiguration = 2103; + ConfManagerErrGenerateKeyCerts = 2200; + +} \ No newline at end of file diff --git a/python/README.md b/python/README.md index c0ee9b71..ae241cee 100644 --- a/python/README.md +++ b/python/README.md @@ -5,6 +5,8 @@ At this moment, this package only contains generated python protos. ## Update Protos ``` +# python=3.10 +pip install -r requirements.txt python setup.py generate_py_protobufs ``` diff --git a/python/kuscia/datamesh/__init__.py b/python/kuscia/datamesh/__init__.py new file mode 100644 index 00000000..2a7347d5 --- /dev/null +++ b/python/kuscia/datamesh/__init__.py @@ -0,0 +1,22 @@ +# Copyright 2024 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from .api import init +from .api import download_to_file +from .api import upload_file +from .api import create_domaindata + + +__all__ = ['init', 'download_to_file', 'create_domaindata', 'upload_file'] \ No newline at end of file diff --git a/python/kuscia/datamesh/api.py b/python/kuscia/datamesh/api.py new file mode 100644 index 00000000..9747a9c1 --- /dev/null +++ b/python/kuscia/datamesh/api.py @@ -0,0 +1,108 @@ +# Copyright 2024 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import pyarrow.flight as flight + +from kuscia.proto.api.v1alpha1.datamesh.domaindata_pb2 import ( + DomainData, +) +from kuscia.proto.api.v1alpha1.common_pb2 import ( + FileFormat, + DataColumn +) + + +from . import dataproxy +from . import datamanager + +DEFAULT_GENERIC_OPTIONS = [("GRPC_ARG_KEEPALIVE_TIME_MS", 60000)] + +class datamesh_client_config: + def __init__(self): + self.address = None + self.client_cert = None + self.client_key = None + self.trusted_ca = None + +_dm_flight_client_config = None + +def is_address_has_scheme(address: str): + return address.startswith("grpc://") or address.startswith("grpcs://") or address.startswith("grpc+tls://") + +def new_datamesh_client(): + global _dm_flight_client_config + if _dm_flight_client_config is None: + raise "datamesh client config is not inited" + + address = _dm_flight_client_config.address + if not is_address_has_scheme(address): + if _dm_flight_client_config.client_cert != None: + address = "grpc+tls://" + address, + else: + address = "grpc://" + address + + if _dm_flight_client_config.client_cert != None: + dm_flight_client = flight.connect(address,) + else: + dm_flight_client = flight.connect( + address, + tls_root_certs=_dm_flight_client_config.trusted_ca, + cert_chain=_dm_flight_client_config.client_cert, + private_key=_dm_flight_client_config.client_key, + generic_options=DEFAULT_GENERIC_OPTIONS, + ) + + return dm_flight_client + +def init(address: str): + global _dm_flight_client_config + if _dm_flight_client_config is not None: + raise "datamesh had inited, can't init again" + + _dm_flight_client_config = datamesh_client_config() + _dm_flight_client_config.address = address + + # load key/cert/ca from env + if os.environ.get("CLIENT_CERT_FILE", '') != '': + with open(os.environ.get("CLIENT_CERT_FILE", ''), 'rb') as file: + _dm_flight_client_config.client_cert = file.read() + if os.environ.get("CLIENT_PRIVATE_KEY_FILE", '') != '': + with open(os.environ.get("CLIENT_PRIVATE_KEY_FILE", ''), 'rb') as file: + _dm_flight_client_config.client_key = file.read() + if os.environ.get("TRUSTED_CA_FILE", '') != '': + with open(os.environ.get("TRUSTED_CA_FILE", ''), 'rb') as file: + _dm_flight_client_config.trusted_ca = file.read() + + + +def create_domaindata(domain_data: DomainData, file_format: FileFormat): + client = new_datamesh_client() + res = datamanager.create_domain_data_in_dp(client, domain_data, file_format) + client.close() + return res + + +def download_to_file(domain_data_id: str, output_file_path: str, file_format: FileFormat = FileFormat.BINARY): + client = new_datamesh_client() + res = dataproxy.get_file_from_dp(client, domain_data_id, output_file_path, file_format) + client.close() + return res + + +def upload_file(domain_data_id: str, input_file_path: str, file_format: FileFormat = FileFormat.BINARY): + client = new_datamesh_client() + res = dataproxy.put_file_to_dp(client, domain_data_id, input_file_path, file_format) + client.close() + return res \ No newline at end of file diff --git a/python/kuscia/datamesh/config.py b/python/kuscia/datamesh/config.py new file mode 100644 index 00000000..7645637f --- /dev/null +++ b/python/kuscia/datamesh/config.py @@ -0,0 +1,19 @@ +# Copyright 2024 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pyarrow.flight as flight + +DEFAULT_FLIGHT_CALL_OPTIONS = flight.FlightCallOptions( + timeout=10 +) # timeout unit is second. \ No newline at end of file diff --git a/python/kuscia/datamesh/datamanager.py b/python/kuscia/datamesh/datamanager.py new file mode 100644 index 00000000..22579b3e --- /dev/null +++ b/python/kuscia/datamesh/datamanager.py @@ -0,0 +1,61 @@ +# Copyright 2024 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import pyarrow.flight as flight + +from kuscia.proto.api.v1alpha1.common_pb2 import FileFormat +from kuscia.proto.api.v1alpha1.datamesh.domaindata_pb2 import ( + CreateDomainDataRequest, + CreateDomainDataResponse, + DomainData, +) + +from . import config + +def create_domain_data_in_dp( + dm_flight_client, + domain_data: DomainData, + file_format: FileFormat, +): + create_domain_data_request = CreateDomainDataRequest( + domaindata_id=domain_data.domaindata_id, + name=domain_data.name, + type=domain_data.type, + datasource_id=domain_data.datasource_id, + relative_uri=domain_data.relative_uri, + attributes=domain_data.attributes, + # partition=data.partition, + columns=domain_data.columns, + vendor=domain_data.vendor, + file_format=file_format, + ) + + action = flight.Action( + "ActionCreateDomainDataRequest", + create_domain_data_request.SerializeToString(), + ) + + results = dm_flight_client.do_action( + action=action, options=config.DEFAULT_FLIGHT_CALL_OPTIONS + ) + + for res in results: + action_response = CreateDomainDataResponse() + action_response.ParseFromString(res.body.to_pybytes()) + logging.info(f"create domain data status=%d", action_response.status.code) + if action_response.status.code == 0: + return action_response.data.domaindata_id + + return None diff --git a/python/kuscia/datamesh/dataproxy.py b/python/kuscia/datamesh/dataproxy.py new file mode 100644 index 00000000..0d233fce --- /dev/null +++ b/python/kuscia/datamesh/dataproxy.py @@ -0,0 +1,158 @@ +# Copyright 2024 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import logging + +import pyarrow as pa +import pyarrow.csv as csv +import pyarrow.flight as flight +from google.protobuf.any_pb2 import Any +from kuscia.proto.api.v1alpha1.common_pb2 import FileFormat +from kuscia.proto.api.v1alpha1.datamesh.flightdm_pb2 import ( + CommandDomainDataQuery, + CommandDomainDataUpdate, + ContentType, + CSVWriteOptions, + FileWriteOptions, +) + +from . import config + +def get_file_from_dp( + dm_flight_client, + domain_data_id: str, + output_file_path: str, + file_format: FileFormat, +): + if file_format == FileFormat.CSV: + domain_data_query = CommandDomainDataQuery( + domaindata_id=domain_data_id, + content_type=ContentType.Table, + ) + elif file_format == FileFormat.BINARY: + domain_data_query = CommandDomainDataQuery( + domaindata_id=domain_data_id, + content_type=ContentType.RAW, + ) + else: + raise AttributeError(f"unknown file_format {file_format}") + + any = Any() + any.Pack(domain_data_query) + + descriptor = flight.FlightDescriptor.for_command(any.SerializeToString()) + + flight_info = dm_flight_client.get_flight_info( + descriptor=descriptor, options=config.DEFAULT_FLIGHT_CALL_OPTIONS + ) + + location = flight_info.endpoints[0].locations[0] + ticket = flight_info.endpoints[0].ticket + dp_uri = location.uri.decode('utf-8') + + if dp_uri.startswith("kuscia://"): + dp_flight_client = dm_flight_client + else: + dp_flight_client = flight.connect(dp_uri, ) + flight_reader = dp_flight_client.do_get(ticket=ticket).to_reader() + if file_format == FileFormat.CSV: + # NOTE(junfeng): use pandas to write csv since pyarrow will add quotes in headers. + logging.info(f"write csv file=%s", output_file_path) + os.remove(output_file_path) + for batch in flight_reader: + batch_pd = batch.to_pandas() + batch_pd.to_csv( + output_file_path, + index=False, + mode='a', + header=not os.path.exists(output_file_path), + ) + elif file_format == FileFormat.BINARY: + with open(output_file_path, "wb") as f: + for batch in flight_reader: + assert batch.num_columns == 1 + array = batch.column(0) + assert array.type == pa.binary() + for r in array: + f.write(r.as_py()) + else: + raise AttributeError(f"unknown file_format {file_format}") + + +def put_file_to_dp( + dm_flight_client, + domaindata_id: str, + file_local_path: str, + file_format: FileFormat, +): + if file_format == FileFormat.CSV: + command_domain_data_update = CommandDomainDataUpdate( + domaindata_id=domaindata_id, + file_write_options=FileWriteOptions( + csv_options=CSVWriteOptions(field_delimiter=",") + ), + ) + reader = csv.open_csv(file_local_path) + schema = reader.schema + elif file_format == FileFormat.BINARY: + command_domain_data_update = CommandDomainDataUpdate( + domaindata_id=domaindata_id, + content_type=ContentType.RAW, + ) + bin_col_name = "binary_data" + + def _bin_reader(): + # 1MB + read_chunks = 8 + chunks = [] + with open(file_local_path, "rb") as f: + for chunk in iter(lambda: f.read(1024 * 128), b''): + chunks.append(chunk) + if len(chunks) >= read_chunks: + yield pa.record_batch([pa.array(chunks)], names=[bin_col_name]) + chunks = [] + if len(chunks): + yield pa.record_batch([pa.array(chunks)], names=[bin_col_name]) + + reader = _bin_reader() + schema = pa.schema([(bin_col_name, pa.binary())]) + else: + raise AttributeError(f"unknown file_format {file_format}") + + any = Any() + any.Pack(command_domain_data_update) + + descriptor = flight.FlightDescriptor.for_command(any.SerializeToString()) + + flight_info = dm_flight_client.get_flight_info( + descriptor=descriptor, options=config.DEFAULT_FLIGHT_CALL_OPTIONS + ) + + location = flight_info.endpoints[0].locations[0] + ticket = flight_info.endpoints[0].ticket + dp_uri = location.uri.decode('utf-8') + if dp_uri.startswith("kuscia://"): + dp_flight_client = dm_flight_client + else: + dp_flight_client = flight.connect(dp_uri, ) + + descriptor = flight.FlightDescriptor.for_command(ticket.ticket) + flight_writer, _ = dp_flight_client.do_put(descriptor=descriptor, schema=schema) + + for batch in reader: + flight_writer.write(batch) + + reader.close() + flight_writer.close() \ No newline at end of file diff --git a/python/kuscia/proto/api/v1alpha1/common_pb2.py b/python/kuscia/proto/api/v1alpha1/common_pb2.py index b613bb6c..56139acf 100644 --- a/python/kuscia/proto/api/v1alpha1/common_pb2.py +++ b/python/kuscia/proto/api/v1alpha1/common_pb2.py @@ -15,7 +15,7 @@ from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&kuscia/proto/api/v1alpha1/common.proto\x12\x19kuscia.proto.api.v1alpha1\x1a\x19google/protobuf/any.proto\"\x9a\x01\n\rRequestHeader\x12S\n\x0e\x63ustom_headers\x18\x01 \x03(\x0b\x32;.kuscia.proto.api.v1alpha1.RequestHeader.CustomHeadersEntry\x1a\x34\n\x12\x43ustomHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"N\n\x06Status\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.Any\"P\n\tPartition\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x35\n\x06\x66ields\x18\x02 \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\"O\n\nDataColumn\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0f\n\x07\x63omment\x18\x03 \x01(\t\x12\x14\n\x0cnot_nullable\x18\x04 \x01(\x08*.\n\nFileFormat\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03\x43SV\x10\x01\x12\n\n\x06\x42INARY\x10\x02\x42Q\n\x1eorg.secretflow.v1alpha1.commonZ/github.com/secretflow/kuscia/proto/api/v1alpha1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&kuscia/proto/api/v1alpha1/common.proto\x12\x19kuscia.proto.api.v1alpha1\x1a\x19google/protobuf/any.proto\"\x9a\x01\n\rRequestHeader\x12S\n\x0e\x63ustom_headers\x18\x01 \x03(\x0b\x32;.kuscia.proto.api.v1alpha1.RequestHeader.CustomHeadersEntry\x1a\x34\n\x12\x43ustomHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"N\n\x06Status\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.Any\"P\n\tPartition\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x35\n\x06\x66ields\x18\x02 \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\"O\n\nDataColumn\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0f\n\x07\x63omment\x18\x03 \x01(\t\x12\x14\n\x0cnot_nullable\x18\x04 \x01(\x08\"B\n\rErrorResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status*.\n\nFileFormat\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03\x43SV\x10\x01\x12\n\n\x06\x42INARY\x10\x02\x42Q\n\x1eorg.secretflow.v1alpha1.commonZ/github.com/secretflow/kuscia/proto/api/v1alpha1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -25,8 +25,8 @@ _globals['DESCRIPTOR']._serialized_options = b'\n\036org.secretflow.v1alpha1.commonZ/github.com/secretflow/kuscia/proto/api/v1alpha1' _globals['_REQUESTHEADER_CUSTOMHEADERSENTRY']._options = None _globals['_REQUESTHEADER_CUSTOMHEADERSENTRY']._serialized_options = b'8\001' - _globals['_FILEFORMAT']._serialized_start=496 - _globals['_FILEFORMAT']._serialized_end=542 + _globals['_FILEFORMAT']._serialized_start=564 + _globals['_FILEFORMAT']._serialized_end=610 _globals['_REQUESTHEADER']._serialized_start=97 _globals['_REQUESTHEADER']._serialized_end=251 _globals['_REQUESTHEADER_CUSTOMHEADERSENTRY']._serialized_start=199 @@ -37,4 +37,6 @@ _globals['_PARTITION']._serialized_end=413 _globals['_DATACOLUMN']._serialized_start=415 _globals['_DATACOLUMN']._serialized_end=494 + _globals['_ERRORRESPONSE']._serialized_start=496 + _globals['_ERRORRESPONSE']._serialized_end=562 # @@protoc_insertion_point(module_scope) diff --git a/python/kuscia/proto/api/v1alpha1/datamesh/domaindata_pb2.py b/python/kuscia/proto/api/v1alpha1/datamesh/domaindata_pb2.py index b2e6d752..a144dade 100644 --- a/python/kuscia/proto/api/v1alpha1/datamesh/domaindata_pb2.py +++ b/python/kuscia/proto/api/v1alpha1/datamesh/domaindata_pb2.py @@ -15,14 +15,14 @@ from kuscia.proto.api.v1alpha1 import common_pb2 as kuscia_dot_proto_dot_api_dot_v1alpha1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n3kuscia/proto/api/v1alpha1/datamesh/domaindata.proto\x12\"kuscia.proto.api.v1alpha1.datamesh\x1a&kuscia/proto/api/v1alpha1/common.proto\"\x84\x04\n\x17\x43reateDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x14\n\x0crelative_uri\x18\x05 \x01(\t\x12\x15\n\rdatasource_id\x18\x06 \x01(\t\x12_\n\nattributes\x18\x07 \x03(\x0b\x32K.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest.AttributesEntry\x12\x37\n\tpartition\x18\x08 \x01(\x0b\x32$.kuscia.proto.api.v1alpha1.Partition\x12\x36\n\x07\x63olumns\x18\t \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\x12\x0e\n\x06vendor\x18\n \x01(\t\x12:\n\x0b\x66ile_format\x18\x0b \x01(\x0e\x32%.kuscia.proto.api.v1alpha1.FileFormat\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9d\x01\n\x18\x43reateDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12N\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32@.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataResponseData\"5\n\x1c\x43reateDomainDataResponseData\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\"\x84\x04\n\x17UpdateDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x14\n\x0crelative_uri\x18\x05 \x01(\t\x12\x15\n\rdatasource_id\x18\x06 \x01(\t\x12_\n\nattributes\x18\x07 \x03(\x0b\x32K.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataRequest.AttributesEntry\x12\x37\n\tpartition\x18\x08 \x01(\x0b\x32$.kuscia.proto.api.v1alpha1.Partition\x12\x36\n\x07\x63olumns\x18\t \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\x12\x0e\n\x06vendor\x18\n \x01(\t\x12:\n\x0b\x66ile_format\x18\x0b \x01(\x0e\x32%.kuscia.proto.api.v1alpha1.FileFormat\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"M\n\x18UpdateDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"j\n\x17\x44\x65leteDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\"M\n\x18\x44\x65leteDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"i\n\x16QueryDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\"\x8a\x01\n\x17QueryDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12<\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32..kuscia.proto.api.v1alpha1.datamesh.DomainData\"\xc0\x03\n\nDomainData\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x14\n\x0crelative_uri\x18\x04 \x01(\t\x12\x15\n\rdatasource_id\x18\x05 \x01(\t\x12R\n\nattributes\x18\x06 \x03(\x0b\x32>.kuscia.proto.api.v1alpha1.datamesh.DomainData.AttributesEntry\x12\x37\n\tpartition\x18\x07 \x01(\x0b\x32$.kuscia.proto.api.v1alpha1.Partition\x12\x36\n\x07\x63olumns\x18\x08 \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\x12\x0e\n\x06vendor\x18\t \x01(\t\x12:\n\x0b\x66ile_format\x18\n \x01(\x0e\x32%.kuscia.proto.api.v1alpha1.FileFormat\x12\x0e\n\x06\x61uthor\x18\x0b \x01(\t\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x32\xd0\x04\n\x11\x44omainDataService\x12\x8d\x01\n\x10\x43reateDomainData\x12;.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest\x1a<.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataResponse\x12\x8a\x01\n\x0fQueryDomainData\x12:.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataRequest\x1a;.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataResponse\x12\x8d\x01\n\x10UpdateDomainData\x12;.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataRequest\x1a<.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataResponse\x12\x8d\x01\n\x10\x44\x65leteDomainData\x12;.kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataRequest\x1a<.kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataResponseB_\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh\x80\x01\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n3kuscia/proto/api/v1alpha1/datamesh/domaindata.proto\x12\"kuscia.proto.api.v1alpha1.datamesh\x1a&kuscia/proto/api/v1alpha1/common.proto\"\x84\x04\n\x17\x43reateDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x14\n\x0crelative_uri\x18\x05 \x01(\t\x12\x15\n\rdatasource_id\x18\x06 \x01(\t\x12_\n\nattributes\x18\x07 \x03(\x0b\x32K.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest.AttributesEntry\x12\x37\n\tpartition\x18\x08 \x01(\x0b\x32$.kuscia.proto.api.v1alpha1.Partition\x12\x36\n\x07\x63olumns\x18\t \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\x12\x0e\n\x06vendor\x18\n \x01(\t\x12:\n\x0b\x66ile_format\x18\x0b \x01(\x0e\x32%.kuscia.proto.api.v1alpha1.FileFormat\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9d\x01\n\x18\x43reateDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12N\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32@.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataResponseData\"5\n\x1c\x43reateDomainDataResponseData\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\"\x84\x04\n\x17UpdateDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x14\n\x0crelative_uri\x18\x05 \x01(\t\x12\x15\n\rdatasource_id\x18\x06 \x01(\t\x12_\n\nattributes\x18\x07 \x03(\x0b\x32K.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataRequest.AttributesEntry\x12\x37\n\tpartition\x18\x08 \x01(\x0b\x32$.kuscia.proto.api.v1alpha1.Partition\x12\x36\n\x07\x63olumns\x18\t \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\x12\x0e\n\x06vendor\x18\n \x01(\t\x12:\n\x0b\x66ile_format\x18\x0b \x01(\x0e\x32%.kuscia.proto.api.v1alpha1.FileFormat\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"M\n\x18UpdateDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"j\n\x17\x44\x65leteDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\"M\n\x18\x44\x65leteDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"i\n\x16QueryDomainDataRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdomaindata_id\x18\x02 \x01(\t\"\x8a\x01\n\x17QueryDomainDataResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12<\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32..kuscia.proto.api.v1alpha1.datamesh.DomainData\"\xc0\x03\n\nDomainData\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x14\n\x0crelative_uri\x18\x04 \x01(\t\x12\x15\n\rdatasource_id\x18\x05 \x01(\t\x12R\n\nattributes\x18\x06 \x03(\x0b\x32>.kuscia.proto.api.v1alpha1.datamesh.DomainData.AttributesEntry\x12\x37\n\tpartition\x18\x07 \x01(\x0b\x32$.kuscia.proto.api.v1alpha1.Partition\x12\x36\n\x07\x63olumns\x18\x08 \x03(\x0b\x32%.kuscia.proto.api.v1alpha1.DataColumn\x12\x0e\n\x06vendor\x18\t \x01(\t\x12:\n\x0b\x66ile_format\x18\n \x01(\x0e\x32%.kuscia.proto.api.v1alpha1.FileFormat\x12\x0e\n\x06\x61uthor\x18\x0b \x01(\t\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x32\xd0\x04\n\x11\x44omainDataService\x12\x8d\x01\n\x10\x43reateDomainData\x12;.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest\x1a<.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataResponse\x12\x8a\x01\n\x0fQueryDomainData\x12:.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataRequest\x1a;.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataResponse\x12\x8d\x01\n\x10UpdateDomainData\x12;.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataRequest\x1a<.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataResponse\x12\x8d\x01\n\x10\x44\x65leteDomainData\x12;.kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataRequest\x1a<.kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataResponseB\\\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datameshb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'kuscia.proto.api.v1alpha1.datamesh.domaindata_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None - _globals['DESCRIPTOR']._serialized_options = b'\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh\200\001\001' + _globals['DESCRIPTOR']._serialized_options = b'\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh' _globals['_CREATEDOMAINDATAREQUEST_ATTRIBUTESENTRY']._options = None _globals['_CREATEDOMAINDATAREQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' _globals['_UPDATEDOMAINDATAREQUEST_ATTRIBUTESENTRY']._options = None diff --git a/python/kuscia/proto/api/v1alpha1/datamesh/domaindatasource_pb2.py b/python/kuscia/proto/api/v1alpha1/datamesh/domaindatasource_pb2.py index 6ab3ba42..3272f004 100644 --- a/python/kuscia/proto/api/v1alpha1/datamesh/domaindatasource_pb2.py +++ b/python/kuscia/proto/api/v1alpha1/datamesh/domaindatasource_pb2.py @@ -15,14 +15,14 @@ from kuscia.proto.api.v1alpha1 import common_pb2 as kuscia_dot_proto_dot_api_dot_v1alpha1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n9kuscia/proto/api/v1alpha1/datamesh/domaindatasource.proto\x12\"kuscia.proto.api.v1alpha1.datamesh\x1a&kuscia/proto/api/v1alpha1/common.proto\"o\n\x1cQueryDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdatasource_id\x18\x02 \x01(\t\"\x96\x01\n\x1dQueryDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12\x42\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.datamesh.DomainDataSource\"\xc2\x01\n\x10\x44omainDataSource\x12\x15\n\rdatasource_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x0e\n\x06status\x18\x04 \x01(\t\x12@\n\x04info\x18\x05 \x01(\x0b\x32\x32.kuscia.proto.api.v1alpha1.datamesh.DataSourceInfo\x12\x10\n\x08info_key\x18\x06 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65ss_directly\x18\x07 \x01(\x08\"\xec\x01\n\x0e\x44\x61taSourceInfo\x12H\n\x07localfs\x18\x01 \x01(\x0b\x32\x37.kuscia.proto.api.v1alpha1.datamesh.LocalDataSourceInfo\x12\x42\n\x03oss\x18\x02 \x01(\x0b\x32\x35.kuscia.proto.api.v1alpha1.datamesh.OssDataSourceInfo\x12L\n\x08\x64\x61tabase\x18\x03 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.datamesh.DatabaseDataSourceInfo\"#\n\x13LocalDataSourceInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\"\xb3\x01\n\x11OssDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0e\n\x06prefix\x18\x03 \x01(\t\x12\x15\n\raccess_key_id\x18\x04 \x01(\t\x12\x19\n\x11\x61\x63\x63\x65ss_key_secret\x18\x05 \x01(\t\x12\x13\n\x0bvirtualhost\x18\x06 \x01(\x08\x12\x0f\n\x07version\x18\x07 \x01(\t\x12\x14\n\x0cstorage_type\x18\x08 \x01(\t\"\\\n\x16\x44\x61tabaseDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x10\n\x08password\x18\x03 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x04 \x01(\t2\xb8\x01\n\x17\x44omainDataSourceService\x12\x9c\x01\n\x15QueryDomainDataSource\x12@.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceRequest\x1a\x41.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceResponseB_\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh\x80\x01\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n9kuscia/proto/api/v1alpha1/datamesh/domaindatasource.proto\x12\"kuscia.proto.api.v1alpha1.datamesh\x1a&kuscia/proto/api/v1alpha1/common.proto\"o\n\x1cQueryDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x15\n\rdatasource_id\x18\x02 \x01(\t\"\x96\x01\n\x1dQueryDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12\x42\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.datamesh.DomainDataSource\"\xc2\x01\n\x10\x44omainDataSource\x12\x15\n\rdatasource_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x0e\n\x06status\x18\x04 \x01(\t\x12@\n\x04info\x18\x05 \x01(\x0b\x32\x32.kuscia.proto.api.v1alpha1.datamesh.DataSourceInfo\x12\x10\n\x08info_key\x18\x06 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65ss_directly\x18\x07 \x01(\x08\"\xec\x01\n\x0e\x44\x61taSourceInfo\x12H\n\x07localfs\x18\x01 \x01(\x0b\x32\x37.kuscia.proto.api.v1alpha1.datamesh.LocalDataSourceInfo\x12\x42\n\x03oss\x18\x02 \x01(\x0b\x32\x35.kuscia.proto.api.v1alpha1.datamesh.OssDataSourceInfo\x12L\n\x08\x64\x61tabase\x18\x03 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.datamesh.DatabaseDataSourceInfo\"#\n\x13LocalDataSourceInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\"\xb3\x01\n\x11OssDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0e\n\x06prefix\x18\x03 \x01(\t\x12\x15\n\raccess_key_id\x18\x04 \x01(\t\x12\x19\n\x11\x61\x63\x63\x65ss_key_secret\x18\x05 \x01(\t\x12\x13\n\x0bvirtualhost\x18\x06 \x01(\x08\x12\x0f\n\x07version\x18\x07 \x01(\t\x12\x14\n\x0cstorage_type\x18\x08 \x01(\t\"\\\n\x16\x44\x61tabaseDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x10\n\x08password\x18\x03 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x04 \x01(\t2\xb8\x01\n\x17\x44omainDataSourceService\x12\x9c\x01\n\x15QueryDomainDataSource\x12@.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceRequest\x1a\x41.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceResponseB\\\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datameshb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'kuscia.proto.api.v1alpha1.datamesh.domaindatasource_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None - _globals['DESCRIPTOR']._serialized_options = b'\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh\200\001\001' + _globals['DESCRIPTOR']._serialized_options = b'\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh' _globals['_QUERYDOMAINDATASOURCEREQUEST']._serialized_start=137 _globals['_QUERYDOMAINDATASOURCEREQUEST']._serialized_end=248 _globals['_QUERYDOMAINDATASOURCERESPONSE']._serialized_start=251 diff --git a/python/kuscia/proto/api/v1alpha1/datamesh/flightdm_pb2.py b/python/kuscia/proto/api/v1alpha1/datamesh/flightdm_pb2.py index eac2ba1b..9d264391 100644 --- a/python/kuscia/proto/api/v1alpha1/datamesh/flightdm_pb2.py +++ b/python/kuscia/proto/api/v1alpha1/datamesh/flightdm_pb2.py @@ -13,10 +13,9 @@ from kuscia.proto.api.v1alpha1.datamesh import domaindata_pb2 as kuscia_dot_proto_dot_api_dot_v1alpha1_dot_datamesh_dot_domaindata__pb2 -from kuscia.proto.api.v1alpha1.datamesh import domaindatasource_pb2 as kuscia_dot_proto_dot_api_dot_v1alpha1_dot_datamesh_dot_domaindatasource__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1kuscia/proto/api/v1alpha1/datamesh/flightdm.proto\x12\"kuscia.proto.api.v1alpha1.datamesh\x1a\x33kuscia/proto/api/v1alpha1/datamesh/domaindata.proto\x1a\x39kuscia/proto/api/v1alpha1/datamesh/domaindatasource.proto\"*\n\x0f\x43SVWriteOptions\x12\x17\n\x0f\x66ield_delimiter\x18\x01 \x01(\t\"i\n\x10\x46ileWriteOptions\x12J\n\x0b\x63sv_options\x18\x02 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.datamesh.CSVWriteOptionsH\x00\x42\t\n\x07Options\"3\n\x1a\x43ommandGetDomainDataSchema\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\"\xd9\x01\n\x16\x43ommandDomainDataQuery\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\x12\x0f\n\x07\x63olumns\x18\x02 \x03(\t\x12\x45\n\x0c\x63ontent_type\x18\x03 \x01(\x0e\x32/.kuscia.proto.api.v1alpha1.datamesh.ContentType\x12P\n\x12\x66ile_write_options\x18\x04 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions\"\xbd\x03\n\x17\x43ommandDomainDataUpdate\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\x12W\n\x12\x64omaindata_request\x18\x02 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest\x12\x45\n\x0c\x63ontent_type\x18\x03 \x01(\x0e\x32/.kuscia.proto.api.v1alpha1.datamesh.ContentType\x12P\n\x12\x66ile_write_options\x18\x04 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions\x12\x64\n\rextra_options\x18\x05 \x03(\x0b\x32M.kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.ExtraOptionsEntry\x1a\x33\n\x11\x45xtraOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"2\n\x15TicketDomainDataQuery\x12\x19\n\x11\x64omaindata_handle\x18\x01 \x01(\t\"m\n\x1d\x41\x63tionCreateDomainDataRequest\x12L\n\x07request\x18\x01 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest\"p\n\x1e\x41\x63tionCreateDomainDataResponse\x12N\n\x08response\x18\x01 \x01(\x0b\x32<.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataResponse\"k\n\x1c\x41\x63tionQueryDomainDataRequest\x12K\n\x07request\x18\x01 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataRequest\"n\n\x1d\x41\x63tionQueryDomainDataResponse\x12M\n\x08response\x18\x01 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataResponse\"m\n\x1d\x41\x63tionUpdateDomainDataRequest\x12L\n\x07request\x18\x01 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataRequest\"p\n\x1e\x41\x63tionUpdateDomainDataResponse\x12N\n\x08response\x18\x01 \x01(\x0b\x32<.kuscia.proto.api.v1alpha1.datamesh.UpdateDomainDataResponse\"\x88\x01\n\x1d\x41\x63tionDeleteDomainDataRequest\x12L\n\x07request\x18\x01 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataRequest\x12\x19\n\x11physical_deletion\x18\x02 \x01(\x08\"p\n\x1e\x41\x63tionDeleteDomainDataResponse\x12N\n\x08response\x18\x01 \x01(\x0b\x32<.kuscia.proto.api.v1alpha1.datamesh.DeleteDomainDataResponse\"w\n\"ActionQueryDomainDataSourceRequest\x12Q\n\x07request\x18\x01 \x01(\x0b\x32@.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceRequest\"z\n#ActionQueryDomainDataSourceResponse\x12S\n\x08response\x18\x01 \x01(\x0b\x32\x41.kuscia.proto.api.v1alpha1.datamesh.QueryDomainDataSourceResponse**\n\x0b\x43ontentType\x12\t\n\x05Table\x10\x00\x12\x07\n\x03RAW\x10\x01\x12\x07\n\x03\x43SV\x10\x02\x42\\\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datameshb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1kuscia/proto/api/v1alpha1/datamesh/flightdm.proto\x12\"kuscia.proto.api.v1alpha1.datamesh\x1a\x33kuscia/proto/api/v1alpha1/datamesh/domaindata.proto\"*\n\x0f\x43SVWriteOptions\x12\x17\n\x0f\x66ield_delimiter\x18\x01 \x01(\t\"i\n\x10\x46ileWriteOptions\x12J\n\x0b\x63sv_options\x18\x02 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.datamesh.CSVWriteOptionsH\x00\x42\t\n\x07Options\"3\n\x1a\x43ommandGetDomainDataSchema\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\"\xd9\x01\n\x16\x43ommandDomainDataQuery\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\x12\x0f\n\x07\x63olumns\x18\x02 \x03(\t\x12\x45\n\x0c\x63ontent_type\x18\x03 \x01(\x0e\x32/.kuscia.proto.api.v1alpha1.datamesh.ContentType\x12P\n\x12\x66ile_write_options\x18\x04 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions\"\xbd\x03\n\x17\x43ommandDomainDataUpdate\x12\x15\n\rdomaindata_id\x18\x01 \x01(\t\x12W\n\x12\x64omaindata_request\x18\x02 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.datamesh.CreateDomainDataRequest\x12\x45\n\x0c\x63ontent_type\x18\x03 \x01(\x0e\x32/.kuscia.proto.api.v1alpha1.datamesh.ContentType\x12P\n\x12\x66ile_write_options\x18\x04 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.datamesh.FileWriteOptions\x12\x64\n\rextra_options\x18\x05 \x03(\x0b\x32M.kuscia.proto.api.v1alpha1.datamesh.CommandDomainDataUpdate.ExtraOptionsEntry\x1a\x33\n\x11\x45xtraOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01**\n\x0b\x43ontentType\x12\t\n\x05Table\x10\x00\x12\x07\n\x03RAW\x10\x01\x12\x07\n\x03\x43SV\x10\x02\x42\\\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datameshb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -26,40 +25,18 @@ _globals['DESCRIPTOR']._serialized_options = b'\n org.secretflow.v1alpha1.datameshZ8github.com/secretflow/kuscia/proto/api/v1alpha1/datamesh' _globals['_COMMANDDOMAINDATAUPDATE_EXTRAOPTIONSENTRY']._options = None _globals['_COMMANDDOMAINDATAUPDATE_EXTRAOPTIONSENTRY']._serialized_options = b'8\001' - _globals['_CONTENTTYPE']._serialized_start=2294 - _globals['_CONTENTTYPE']._serialized_end=2336 - _globals['_CSVWRITEOPTIONS']._serialized_start=201 - _globals['_CSVWRITEOPTIONS']._serialized_end=243 - _globals['_FILEWRITEOPTIONS']._serialized_start=245 - _globals['_FILEWRITEOPTIONS']._serialized_end=350 - _globals['_COMMANDGETDOMAINDATASCHEMA']._serialized_start=352 - _globals['_COMMANDGETDOMAINDATASCHEMA']._serialized_end=403 - _globals['_COMMANDDOMAINDATAQUERY']._serialized_start=406 - _globals['_COMMANDDOMAINDATAQUERY']._serialized_end=623 - _globals['_COMMANDDOMAINDATAUPDATE']._serialized_start=626 - _globals['_COMMANDDOMAINDATAUPDATE']._serialized_end=1071 - _globals['_COMMANDDOMAINDATAUPDATE_EXTRAOPTIONSENTRY']._serialized_start=1020 - _globals['_COMMANDDOMAINDATAUPDATE_EXTRAOPTIONSENTRY']._serialized_end=1071 - _globals['_TICKETDOMAINDATAQUERY']._serialized_start=1073 - _globals['_TICKETDOMAINDATAQUERY']._serialized_end=1123 - _globals['_ACTIONCREATEDOMAINDATAREQUEST']._serialized_start=1125 - _globals['_ACTIONCREATEDOMAINDATAREQUEST']._serialized_end=1234 - _globals['_ACTIONCREATEDOMAINDATARESPONSE']._serialized_start=1236 - _globals['_ACTIONCREATEDOMAINDATARESPONSE']._serialized_end=1348 - _globals['_ACTIONQUERYDOMAINDATAREQUEST']._serialized_start=1350 - _globals['_ACTIONQUERYDOMAINDATAREQUEST']._serialized_end=1457 - _globals['_ACTIONQUERYDOMAINDATARESPONSE']._serialized_start=1459 - _globals['_ACTIONQUERYDOMAINDATARESPONSE']._serialized_end=1569 - _globals['_ACTIONUPDATEDOMAINDATAREQUEST']._serialized_start=1571 - _globals['_ACTIONUPDATEDOMAINDATAREQUEST']._serialized_end=1680 - _globals['_ACTIONUPDATEDOMAINDATARESPONSE']._serialized_start=1682 - _globals['_ACTIONUPDATEDOMAINDATARESPONSE']._serialized_end=1794 - _globals['_ACTIONDELETEDOMAINDATAREQUEST']._serialized_start=1797 - _globals['_ACTIONDELETEDOMAINDATAREQUEST']._serialized_end=1933 - _globals['_ACTIONDELETEDOMAINDATARESPONSE']._serialized_start=1935 - _globals['_ACTIONDELETEDOMAINDATARESPONSE']._serialized_end=2047 - _globals['_ACTIONQUERYDOMAINDATASOURCEREQUEST']._serialized_start=2049 - _globals['_ACTIONQUERYDOMAINDATASOURCEREQUEST']._serialized_end=2168 - _globals['_ACTIONQUERYDOMAINDATASOURCERESPONSE']._serialized_start=2170 - _globals['_ACTIONQUERYDOMAINDATASOURCERESPONSE']._serialized_end=2292 + _globals['_CONTENTTYPE']._serialized_start=1014 + _globals['_CONTENTTYPE']._serialized_end=1056 + _globals['_CSVWRITEOPTIONS']._serialized_start=142 + _globals['_CSVWRITEOPTIONS']._serialized_end=184 + _globals['_FILEWRITEOPTIONS']._serialized_start=186 + _globals['_FILEWRITEOPTIONS']._serialized_end=291 + _globals['_COMMANDGETDOMAINDATASCHEMA']._serialized_start=293 + _globals['_COMMANDGETDOMAINDATASCHEMA']._serialized_end=344 + _globals['_COMMANDDOMAINDATAQUERY']._serialized_start=347 + _globals['_COMMANDDOMAINDATAQUERY']._serialized_end=564 + _globals['_COMMANDDOMAINDATAUPDATE']._serialized_start=567 + _globals['_COMMANDDOMAINDATAUPDATE']._serialized_end=1012 + _globals['_COMMANDDOMAINDATAUPDATE_EXTRAOPTIONSENTRY']._serialized_start=961 + _globals['_COMMANDDOMAINDATAUPDATE_EXTRAOPTIONSENTRY']._serialized_end=1012 # @@protoc_insertion_point(module_scope) diff --git a/python/kuscia/proto/api/v1alpha1/errorcode/__init__.py b/python/kuscia/proto/api/v1alpha1/errorcode/__init__.py new file mode 100644 index 00000000..8999a28b --- /dev/null +++ b/python/kuscia/proto/api/v1alpha1/errorcode/__init__.py @@ -0,0 +1,14 @@ + +# Copyright 2023 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2.py b/python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2.py new file mode 100644 index 00000000..4feb7614 --- /dev/null +++ b/python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: kuscia/proto/api/v1alpha1/errorcode/error_code.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n4kuscia/proto/api/v1alpha1/errorcode/error_code.proto\x12\x19kuscia.proto.api.v1alpha1*\xe4\x19\n\tErrorCode\x12\x0b\n\x07SUCCESS\x10\x00\x12 \n\x1bKusciaAPIErrRequestValidate\x10\xdcV\x12\x1e\n\x19KusciaAPIErrForUnexpected\x10\xddV\x12\x1b\n\x16KusciaAPIErrAuthFailed\x10\xdeV\x12$\n\x1fKusciaAPIErrRequestMasterFailed\x10\xdfV\x12\"\n\x1dKusciaAPIErrLiteAPINotSupport\x10\xe0V\x12\x1a\n\x15KusciaAPIErrCreateJob\x10\xc1W\x12\x19\n\x14KusciaAPIErrQueryJob\x10\xc2W\x12\x1f\n\x1aKusciaAPIErrQueryJobStatus\x10\xc3W\x12\x1a\n\x15KusciaAPIErrDeleteJob\x10\xc4W\x12\x18\n\x13KusciaAPIErrStopJob\x10\xc5W\x12\x1b\n\x16KusciaAPIErrApproveJob\x10\xc6W\x12\x1b\n\x16KusciaAPIErrSuspendJob\x10\xc7W\x12\x1b\n\x16KusciaAPIErrRestartJob\x10\xc8W\x12\x1a\n\x15KusciaAPIErrCancelJob\x10\xc9W\x12%\n KusciaAPIErrSuspendNotRunningJob\x10\xcaW\x12/\n*KusciaAPIErrRestartNotSuspendedOrFailedJob\x10\xcbW\x12\x1d\n\x18KusciaAPIErrCreateDomain\x10\xa4X\x12\x1c\n\x17KusciaAPIErrQueryDomain\x10\xa5X\x12\"\n\x1dKusciaAPIErrQueryDomainStatus\x10\xa6X\x12\x1d\n\x18KusciaAPIErrUpdateDomain\x10\xa7X\x12\x1d\n\x18KusciaAPIErrDeleteDomain\x10\xa8X\x12 \n\x1bKusciaAPIErrDomainNotExists\x10\xa9X\x12\x1d\n\x18KusciaAPIErrDomainExists\x10\xaaX\x12\"\n\x1dKusciaAPIErrCreateDomainRoute\x10\x88Y\x12!\n\x1cKusciaAPIErrQueryDomainRoute\x10\x89Y\x12\'\n\"KusciaAPIErrQueryDomainRouteStatus\x10\x8aY\x12\"\n\x1dKusciaAPIErrDeleteDomainRoute\x10\x8bY\x12%\n KusciaAPIErrDomainRouteNotExists\x10\x8cY\x12\"\n\x1dKusciaAPIErrDomainRouteExists\x10\x8dY\x12\'\n\"KusciaAPIErrCreateDomainDataFailed\x10\xecY\x12\'\n\"KusciaAPIErrDeleteDomainDataFailed\x10\xedY\x12$\n\x1fKusciaAPIErrGetDomainDataFailed\x10\xeeY\x12%\n KusciaAPIErrListDomainDataFailed\x10\xefY\x12&\n!KusciaAPIErrMergeDomainDataFailed\x10\xf0Y\x12&\n!KusciaAPIErrPatchDomainDataFailed\x10\xf1Y\x12$\n\x1fKusciaAPIErrDomainDataNotExists\x10\xf2Y\x12!\n\x1cKusciaAPIErrDomainDataExists\x10\xf3Y\x12\x1e\n\x19KusciaAPIErrCreateServing\x10\xd0Z\x12\x1d\n\x18KusciaAPIErrQueryServing\x10\xd1Z\x12#\n\x1eKusciaAPIErrQueryServingStatus\x10\xd2Z\x12\x1e\n\x19KusciaAPIErrUpdateServing\x10\xd3Z\x12\x1e\n\x19KusciaAPIErrDeleteServing\x10\xd4Z\x12&\n!KusciaAPIErrCreateDomainDataGrant\x10\xb4[\x12&\n!KusciaAPIErrUpdateDomainDataGrant\x10\xb5[\x12%\n KusciaAPIErrQueryDomainDataGrant\x10\xb6[\x12&\n!KusciaAPIErrDeleteDomainDataGrant\x10\xb7[\x12&\n!KusciaAPIErrDomainDataGrantExists\x10\xb8[\x12)\n$KusciaAPIErrDomainDataGrantNotExists\x10\xb9[\x12\'\n\"KusciaAPIErrCreateDomainDataSource\x10\x98\\\x12\'\n\"KusciaAPIErrUpdateDomainDataSource\x10\x99\\\x12&\n!KusciaAPIErrQueryDomainDataSource\x10\x9a\\\x12+\n&KusciaAPIErrBatchQueryDomainDataSource\x10\x9b\\\x12\'\n\"KusciaAPIErrDeleteDomainDataSource\x10\x9c\\\x12\'\n\"KusciaAPIErrDomainDataSourceExists\x10\x9d\\\x12*\n%KusciaAPIErrDomainDataSourceNotExists\x10\x9e\\\x12\x31\n,KusciaAPIErrDomainDataSourceInfoEncodeFailed\x10\x9f\\\x12%\n KusciaAPIErrListDomainDataSource\x10\xa0\\\x12!\n\x1c\x44\x61taMeshErrRequestInvalidate\x10\xc4^\x12\x1d\n\x18\x44\x61taMeshErrForUnexpected\x10\xc5^\x12 \n\x1b\x44\x61taMeshErrCreateDomainData\x10\xa8_\x12\x1f\n\x1a\x44\x61taMeshErrQueryDomainData\x10\xa9_\x12+\n&DataMeshErrGetDomainDataFromKubeFailed\x10\xaa_\x12%\n DataMeshErrMergeDomainDataFailed\x10\xab_\x12%\n DataMeshErrPatchDomainDataFailed\x10\xac_\x12&\n!DataMeshErrDeleteDomainDataFailed\x10\xad_\x12&\n!DataMeshErrCreateDomainDataSource\x10\x8c`\x12+\n&DataMeshErrParseDomainDataSourceFailed\x10\x8d`\x12%\n DataMeshErrQueryDomainDataSource\x10\x8e`\x12,\n\'DataMeshErrDeleteDomainDataSourceFailed\x10\x8f`\x12&\n!DataMeshErrDomainDataSourceExists\x10\x90`\x12)\n$DataMeshErrDomainDataSourceNotExists\x10\x91`\x12\x31\n,DataMeshErrGetDomainDataSourceFromKubeFailed\x10\x92`\x12\x30\n+DataMeshErrDecodeDomainDataSourceInfoFailed\x10\x93`\x12\x30\n+DataMeshErrEncodeDomainDataSourceInfoFailed\x10\x94`\x12+\n&DataMeshErrMergeDomainDataSourceFailed\x10\x95`\x12+\n&DataMeshErrPatchDomainDataSourceFailed\x10\x96`\x12%\n DataMeshErrCreateDomainDataGrant\x10\xf0`\x12%\n DataMeshErrUpdateDomainDataGrant\x10\xf1`\x12$\n\x1f\x44\x61taMeshErrQueryDomainDataGrant\x10\xf2`\x12%\n DataMeshErrDeleteDomainDataGrant\x10\xf3`\x12%\n DataMeshErrDomainDataGrantExists\x10\xf4`\x12(\n#DataMeshErrDomainDataGrantNotExists\x10\xf5`\x12$\n\x1f\x43onfManagerErrRequestInvalidate\x10\xd0\x0f\x12 \n\x1b\x43onfManagerErrForUnexpected\x10\xd1\x0f\x12&\n!ConfManagerErrCreateConfiguration\x10\xb6\x10\x12%\n ConfManagerErrQueryConfiguration\x10\xb7\x10\x12#\n\x1e\x43onfManagerErrGenerateKeyCerts\x10\x98\x11\x42^\n!org.secretflow.v1alpha1.errorcodeZ9github.com/secretflow/kuscia/proto/api/v1alpha1/errorcodeb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'kuscia.proto.api.v1alpha1.errorcode.error_code_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\n!org.secretflow.v1alpha1.errorcodeZ9github.com/secretflow/kuscia/proto/api/v1alpha1/errorcode' + _globals['_ERRORCODE']._serialized_start=84 + _globals['_ERRORCODE']._serialized_end=3384 +# @@protoc_insertion_point(module_scope) diff --git a/python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2_grpc.py b/python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2_grpc.py new file mode 100644 index 00000000..8a939394 --- /dev/null +++ b/python/kuscia/proto/api/v1alpha1/errorcode/error_code_pb2_grpc.py @@ -0,0 +1,3 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc diff --git a/python/kuscia/proto/api/v1alpha1/kusciaapi/domain_route_pb2.py b/python/kuscia/proto/api/v1alpha1/kusciaapi/domain_route_pb2.py index d993c3a0..b3af513b 100644 --- a/python/kuscia/proto/api/v1alpha1/kusciaapi/domain_route_pb2.py +++ b/python/kuscia/proto/api/v1alpha1/kusciaapi/domain_route_pb2.py @@ -15,7 +15,7 @@ from kuscia.proto.api.v1alpha1 import common_pb2 as kuscia_dot_proto_dot_api_dot_v1alpha1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n6kuscia/proto/api/v1alpha1/kusciaapi/domain_route.proto\x12#kuscia.proto.api.v1alpha1.kusciaapi\x1a&kuscia/proto/api/v1alpha1/common.proto\"\xea\x02\n\x18\x43reateDomainRouteRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x1b\n\x13\x61uthentication_type\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\t\x12\x44\n\x08\x65ndpoint\x18\x04 \x01(\x0b\x32\x32.kuscia.proto.api.v1alpha1.kusciaapi.RouteEndpoint\x12\x0e\n\x06source\x18\x05 \x01(\t\x12\x46\n\x0ctoken_config\x18\x06 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.TokenConfig\x12\x44\n\x0bmtls_config\x18\x07 \x01(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.MTLSConfig\"_\n\rRouteEndpoint\x12\x0c\n\x04host\x18\x01 \x01(\t\x12@\n\x05ports\x18\x02 \x03(\x0b\x32\x31.kuscia.proto.api.v1alpha1.kusciaapi.EndpointPort\"<\n\x0c\x45ndpointPort\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x05\x12\x10\n\x08protocol\x18\x03 \x01(\t\"\x81\x01\n\x0bTokenConfig\x12\x1e\n\x16\x64\x65stination_public_key\x18\x01 \x01(\t\x12\x1d\n\x15rolling_update_period\x18\x02 \x01(\x03\x12\x19\n\x11source_public_key\x18\x03 \x01(\t\x12\x18\n\x10token_gen_method\x18\x04 \x01(\t\"[\n\nMTLSConfig\x12\x0e\n\x06tls_ca\x18\x01 \x01(\t\x12!\n\x19source_client_private_key\x18\x02 \x01(\t\x12\x1a\n\x12source_client_cert\x18\x03 \x01(\t\"\xa0\x01\n\x19\x43reateDomainRouteResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12P\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x42.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainRouteResponseData\"-\n\x1d\x43reateDomainRouteResponseData\x12\x0c\n\x04name\x18\x01 \x01(\t\"y\n\x18\x44\x65leteDomainRouteRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"N\n\x19\x44\x65leteDomainRouteResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"x\n\x17QueryDomainRouteRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\t\x12\x0e\n\x06source\x18\x03 \x01(\t\"\x9e\x01\n\x18QueryDomainRouteResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12O\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x41.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainRouteResponseData\"\x84\x03\n\x1cQueryDomainRouteResponseData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x61uthentication_type\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\t\x12\x44\n\x08\x65ndpoint\x18\x04 \x01(\x0b\x32\x32.kuscia.proto.api.v1alpha1.kusciaapi.RouteEndpoint\x12\x0e\n\x06source\x18\x05 \x01(\t\x12\x46\n\x0ctoken_config\x18\x06 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.TokenConfig\x12\x44\n\x0bmtls_config\x18\x07 \x01(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.MTLSConfig\x12@\n\x06status\x18\x08 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.RouteStatus\"-\n\x0bRouteStatus\x12\x0e\n\x06status\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\"\xa7\x01\n\"BatchQueryDomainRouteStatusRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12G\n\nroute_keys\x18\x02 \x03(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DomainRouteKey\"5\n\x0e\x44omainRouteKey\x12\x0e\n\x06source\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\t\"\xb4\x01\n#BatchQueryDomainRouteStatusResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12Z\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32L.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainRouteStatusResponseData\"q\n\'BatchQueryDomainRouteStatusResponseData\x12\x46\n\x06routes\x18\x01 \x03(\x0b\x32\x36.kuscia.proto.api.v1alpha1.kusciaapi.DomainRouteStatus\"\x88\x01\n\x11\x44omainRouteStatus\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\t\x12\x0e\n\x06source\x18\x03 \x01(\t\x12@\n\x06status\x18\x04 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.RouteStatus*)\n\x12\x41uthenticationType\x12\t\n\x05Token\x10\x00\x12\x08\n\x04MTLS\x10\x01\x32\x83\x05\n\x12\x44omainRouteService\x12\x92\x01\n\x11\x43reateDomainRoute\x12=.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainRouteRequest\x1a>.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainRouteResponse\x12\x92\x01\n\x11\x44\x65leteDomainRoute\x12=.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainRouteRequest\x1a>.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainRouteResponse\x12\x8f\x01\n\x10QueryDomainRoute\x12<.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainRouteRequest\x1a=.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainRouteResponse\x12\xb0\x01\n\x1b\x42\x61tchQueryDomainRouteStatus\x12G.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainRouteStatusRequest\x1aH.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainRouteStatusResponseB^\n!org.secretflow.v1alpha1.kusciaapiZ9github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapib\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n6kuscia/proto/api/v1alpha1/kusciaapi/domain_route.proto\x12#kuscia.proto.api.v1alpha1.kusciaapi\x1a&kuscia/proto/api/v1alpha1/common.proto\"\xf7\x03\n\x18\x43reateDomainRouteRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x1b\n\x13\x61uthentication_type\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\t\x12\x44\n\x08\x65ndpoint\x18\x04 \x01(\x0b\x32\x32.kuscia.proto.api.v1alpha1.kusciaapi.RouteEndpoint\x12\x0e\n\x06source\x18\x05 \x01(\t\x12\x46\n\x0ctoken_config\x18\x06 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.TokenConfig\x12\x44\n\x0bmtls_config\x18\x07 \x01(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.MTLSConfig\x12=\n\x07transit\x18\x08 \x01(\x0b\x32,.kuscia.proto.api.v1alpha1.kusciaapi.Transit\x12L\n\x0f\x62ody_encryption\x18\t \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.BodyEncryption\"_\n\rRouteEndpoint\x12\x0c\n\x04host\x18\x01 \x01(\t\x12@\n\x05ports\x18\x02 \x03(\x0b\x32\x31.kuscia.proto.api.v1alpha1.kusciaapi.EndpointPort\"`\n\x0c\x45ndpointPort\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\x05\x12\x10\n\x08protocol\x18\x03 \x01(\t\x12\r\n\x05isTLS\x18\x04 \x01(\x08\x12\x13\n\x0bpath_prefix\x18\x05 \x01(\t\"\x81\x01\n\x0bTokenConfig\x12\x1e\n\x16\x64\x65stination_public_key\x18\x01 \x01(\t\x12\x1d\n\x15rolling_update_period\x18\x02 \x01(\x03\x12\x19\n\x11source_public_key\x18\x03 \x01(\t\x12\x18\n\x10token_gen_method\x18\x04 \x01(\t\"[\n\nMTLSConfig\x12\x0e\n\x06tls_ca\x18\x01 \x01(\t\x12!\n\x19source_client_private_key\x18\x02 \x01(\t\x12\x1a\n\x12source_client_cert\x18\x03 \x01(\t\"\x83\x01\n\x07Transit\x12\x16\n\x0etransit_method\x18\x01 \x01(\t\x12\x43\n\x06\x64omain\x18\x02 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.Transit.Domain\x1a\x1b\n\x06\x44omain\x12\x11\n\tdomain_id\x18\x01 \x01(\t\"#\n\x0e\x42odyEncryption\x12\x11\n\talgorithm\x18\x01 \x01(\t\"\xa0\x01\n\x19\x43reateDomainRouteResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12P\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x42.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainRouteResponseData\"-\n\x1d\x43reateDomainRouteResponseData\x12\x0c\n\x04name\x18\x01 \x01(\t\"y\n\x18\x44\x65leteDomainRouteRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"N\n\x19\x44\x65leteDomainRouteResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"x\n\x17QueryDomainRouteRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\t\x12\x0e\n\x06source\x18\x03 \x01(\t\"\x9e\x01\n\x18QueryDomainRouteResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12O\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x41.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainRouteResponseData\"\x91\x04\n\x1cQueryDomainRouteResponseData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x61uthentication_type\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x03 \x01(\t\x12\x44\n\x08\x65ndpoint\x18\x04 \x01(\x0b\x32\x32.kuscia.proto.api.v1alpha1.kusciaapi.RouteEndpoint\x12\x0e\n\x06source\x18\x05 \x01(\t\x12\x46\n\x0ctoken_config\x18\x06 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.TokenConfig\x12\x44\n\x0bmtls_config\x18\x07 \x01(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.MTLSConfig\x12@\n\x06status\x18\x08 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.RouteStatus\x12=\n\x07transit\x18\t \x01(\x0b\x32,.kuscia.proto.api.v1alpha1.kusciaapi.Transit\x12L\n\x0f\x62ody_encryption\x18\n \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.BodyEncryption\"-\n\x0bRouteStatus\x12\x0e\n\x06status\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\"\xa7\x01\n\"BatchQueryDomainRouteStatusRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12G\n\nroute_keys\x18\x02 \x03(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DomainRouteKey\"5\n\x0e\x44omainRouteKey\x12\x0e\n\x06source\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\t\"\xb4\x01\n#BatchQueryDomainRouteStatusResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12Z\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32L.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainRouteStatusResponseData\"q\n\'BatchQueryDomainRouteStatusResponseData\x12\x46\n\x06routes\x18\x01 \x03(\x0b\x32\x36.kuscia.proto.api.v1alpha1.kusciaapi.DomainRouteStatus\"\x88\x01\n\x11\x44omainRouteStatus\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\t\x12\x0e\n\x06source\x18\x03 \x01(\t\x12@\n\x06status\x18\x04 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.RouteStatus*)\n\x12\x41uthenticationType\x12\t\n\x05Token\x10\x00\x12\x08\n\x04MTLS\x10\x01*/\n\x1b\x42odyEncryptionAlgorithmType\x12\x07\n\x03\x41\x45S\x10\x00\x12\x07\n\x03SM4\x10\x01\x32\x83\x05\n\x12\x44omainRouteService\x12\x92\x01\n\x11\x43reateDomainRoute\x12=.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainRouteRequest\x1a>.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainRouteResponse\x12\x92\x01\n\x11\x44\x65leteDomainRoute\x12=.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainRouteRequest\x1a>.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainRouteResponse\x12\x8f\x01\n\x10QueryDomainRoute\x12<.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainRouteRequest\x1a=.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainRouteResponse\x12\xb0\x01\n\x1b\x42\x61tchQueryDomainRouteStatus\x12G.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainRouteStatusRequest\x1aH.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainRouteStatusResponseB^\n!org.secretflow.v1alpha1.kusciaapiZ9github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapib\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -23,44 +23,52 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\n!org.secretflow.v1alpha1.kusciaapiZ9github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapi' - _globals['_AUTHENTICATIONTYPE']._serialized_start=2680 - _globals['_AUTHENTICATIONTYPE']._serialized_end=2721 + _globals['_AUTHENTICATIONTYPE']._serialized_start=3169 + _globals['_AUTHENTICATIONTYPE']._serialized_end=3210 + _globals['_BODYENCRYPTIONALGORITHMTYPE']._serialized_start=3212 + _globals['_BODYENCRYPTIONALGORITHMTYPE']._serialized_end=3259 _globals['_CREATEDOMAINROUTEREQUEST']._serialized_start=136 - _globals['_CREATEDOMAINROUTEREQUEST']._serialized_end=498 - _globals['_ROUTEENDPOINT']._serialized_start=500 - _globals['_ROUTEENDPOINT']._serialized_end=595 - _globals['_ENDPOINTPORT']._serialized_start=597 - _globals['_ENDPOINTPORT']._serialized_end=657 - _globals['_TOKENCONFIG']._serialized_start=660 - _globals['_TOKENCONFIG']._serialized_end=789 - _globals['_MTLSCONFIG']._serialized_start=791 - _globals['_MTLSCONFIG']._serialized_end=882 - _globals['_CREATEDOMAINROUTERESPONSE']._serialized_start=885 - _globals['_CREATEDOMAINROUTERESPONSE']._serialized_end=1045 - _globals['_CREATEDOMAINROUTERESPONSEDATA']._serialized_start=1047 - _globals['_CREATEDOMAINROUTERESPONSEDATA']._serialized_end=1092 - _globals['_DELETEDOMAINROUTEREQUEST']._serialized_start=1094 - _globals['_DELETEDOMAINROUTEREQUEST']._serialized_end=1215 - _globals['_DELETEDOMAINROUTERESPONSE']._serialized_start=1217 - _globals['_DELETEDOMAINROUTERESPONSE']._serialized_end=1295 - _globals['_QUERYDOMAINROUTEREQUEST']._serialized_start=1297 - _globals['_QUERYDOMAINROUTEREQUEST']._serialized_end=1417 - _globals['_QUERYDOMAINROUTERESPONSE']._serialized_start=1420 - _globals['_QUERYDOMAINROUTERESPONSE']._serialized_end=1578 - _globals['_QUERYDOMAINROUTERESPONSEDATA']._serialized_start=1581 - _globals['_QUERYDOMAINROUTERESPONSEDATA']._serialized_end=1969 - _globals['_ROUTESTATUS']._serialized_start=1971 - _globals['_ROUTESTATUS']._serialized_end=2016 - _globals['_BATCHQUERYDOMAINROUTESTATUSREQUEST']._serialized_start=2019 - _globals['_BATCHQUERYDOMAINROUTESTATUSREQUEST']._serialized_end=2186 - _globals['_DOMAINROUTEKEY']._serialized_start=2188 - _globals['_DOMAINROUTEKEY']._serialized_end=2241 - _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSE']._serialized_start=2244 - _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSE']._serialized_end=2424 - _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSEDATA']._serialized_start=2426 - _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSEDATA']._serialized_end=2539 - _globals['_DOMAINROUTESTATUS']._serialized_start=2542 - _globals['_DOMAINROUTESTATUS']._serialized_end=2678 - _globals['_DOMAINROUTESERVICE']._serialized_start=2724 - _globals['_DOMAINROUTESERVICE']._serialized_end=3367 + _globals['_CREATEDOMAINROUTEREQUEST']._serialized_end=639 + _globals['_ROUTEENDPOINT']._serialized_start=641 + _globals['_ROUTEENDPOINT']._serialized_end=736 + _globals['_ENDPOINTPORT']._serialized_start=738 + _globals['_ENDPOINTPORT']._serialized_end=834 + _globals['_TOKENCONFIG']._serialized_start=837 + _globals['_TOKENCONFIG']._serialized_end=966 + _globals['_MTLSCONFIG']._serialized_start=968 + _globals['_MTLSCONFIG']._serialized_end=1059 + _globals['_TRANSIT']._serialized_start=1062 + _globals['_TRANSIT']._serialized_end=1193 + _globals['_TRANSIT_DOMAIN']._serialized_start=1166 + _globals['_TRANSIT_DOMAIN']._serialized_end=1193 + _globals['_BODYENCRYPTION']._serialized_start=1195 + _globals['_BODYENCRYPTION']._serialized_end=1230 + _globals['_CREATEDOMAINROUTERESPONSE']._serialized_start=1233 + _globals['_CREATEDOMAINROUTERESPONSE']._serialized_end=1393 + _globals['_CREATEDOMAINROUTERESPONSEDATA']._serialized_start=1395 + _globals['_CREATEDOMAINROUTERESPONSEDATA']._serialized_end=1440 + _globals['_DELETEDOMAINROUTEREQUEST']._serialized_start=1442 + _globals['_DELETEDOMAINROUTEREQUEST']._serialized_end=1563 + _globals['_DELETEDOMAINROUTERESPONSE']._serialized_start=1565 + _globals['_DELETEDOMAINROUTERESPONSE']._serialized_end=1643 + _globals['_QUERYDOMAINROUTEREQUEST']._serialized_start=1645 + _globals['_QUERYDOMAINROUTEREQUEST']._serialized_end=1765 + _globals['_QUERYDOMAINROUTERESPONSE']._serialized_start=1768 + _globals['_QUERYDOMAINROUTERESPONSE']._serialized_end=1926 + _globals['_QUERYDOMAINROUTERESPONSEDATA']._serialized_start=1929 + _globals['_QUERYDOMAINROUTERESPONSEDATA']._serialized_end=2458 + _globals['_ROUTESTATUS']._serialized_start=2460 + _globals['_ROUTESTATUS']._serialized_end=2505 + _globals['_BATCHQUERYDOMAINROUTESTATUSREQUEST']._serialized_start=2508 + _globals['_BATCHQUERYDOMAINROUTESTATUSREQUEST']._serialized_end=2675 + _globals['_DOMAINROUTEKEY']._serialized_start=2677 + _globals['_DOMAINROUTEKEY']._serialized_end=2730 + _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSE']._serialized_start=2733 + _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSE']._serialized_end=2913 + _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSEDATA']._serialized_start=2915 + _globals['_BATCHQUERYDOMAINROUTESTATUSRESPONSEDATA']._serialized_end=3028 + _globals['_DOMAINROUTESTATUS']._serialized_start=3031 + _globals['_DOMAINROUTESTATUS']._serialized_end=3167 + _globals['_DOMAINROUTESERVICE']._serialized_start=3262 + _globals['_DOMAINROUTESERVICE']._serialized_end=3905 # @@protoc_insertion_point(module_scope) diff --git a/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2.py b/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2.py index 080e77c6..cacaa755 100644 --- a/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2.py +++ b/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2.py @@ -15,7 +15,7 @@ from kuscia.proto.api.v1alpha1 import common_pb2 as kuscia_dot_proto_dot_api_dot_v1alpha1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource.proto\x12#kuscia.proto.api.v1alpha1.kusciaapi\x1a&kuscia/proto/api/v1alpha1/common.proto\"\xd4\x02\n\x1d\x43reateDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x11\n\x04name\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x46\n\x04info\x18\x06 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DataSourceInfoH\x01\x88\x01\x01\x12\x15\n\x08info_key\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x1c\n\x0f\x61\x63\x63\x65ss_directly\x18\x08 \x01(\x08H\x03\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_infoB\x0b\n\t_info_keyB\x12\n\x10_access_directly\"\xaa\x01\n\x1e\x43reateDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12U\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32G.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainDataSourceResponseData\";\n\"CreateDomainDataSourceResponseData\x12\x15\n\rdatasource_id\x18\x01 \x01(\t\"\xd4\x02\n\x1dUpdateDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x11\n\x04name\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x46\n\x04info\x18\x06 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DataSourceInfoH\x01\x88\x01\x01\x12\x15\n\x08info_key\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x1c\n\x0f\x61\x63\x63\x65ss_directly\x18\x08 \x01(\x08H\x03\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_infoB\x0b\n\t_info_keyB\x12\n\x10_access_directly\"S\n\x1eUpdateDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"\x83\x01\n\x1d\x44\x65leteDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\"S\n\x1e\x44\x65leteDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"\x82\x01\n\x1cQueryDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\"\x97\x01\n\x1dQueryDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12\x43\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSource\"L\n QueryDomainDataSourceRequestData\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\x15\n\rdatasource_id\x18\x02 \x01(\t\"\xb2\x01\n!BatchQueryDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12S\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x45.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainDataSourceRequestData\"\xa0\x01\n\"BatchQueryDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12G\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x39.kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSourceList\"f\n\x14\x44omainDataSourceList\x12N\n\x0f\x64\x61tasource_list\x18\x01 \x03(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSource\"\xd6\x01\n\x10\x44omainDataSource\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\x15\n\rdatasource_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\x12\x41\n\x04info\x18\x06 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DataSourceInfo\x12\x10\n\x08info_key\x18\x07 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65ss_directly\x18\x08 \x01(\x08\"\xef\x01\n\x0e\x44\x61taSourceInfo\x12I\n\x07localfs\x18\x01 \x01(\x0b\x32\x38.kuscia.proto.api.v1alpha1.kusciaapi.LocalDataSourceInfo\x12\x43\n\x03oss\x18\x02 \x01(\x0b\x32\x36.kuscia.proto.api.v1alpha1.kusciaapi.OssDataSourceInfo\x12M\n\x08\x64\x61tabase\x18\x03 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.kusciaapi.DatabaseDataSourceInfo\"#\n\x13LocalDataSourceInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\"\xb3\x01\n\x11OssDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0e\n\x06prefix\x18\x03 \x01(\t\x12\x15\n\raccess_key_id\x18\x04 \x01(\t\x12\x19\n\x11\x61\x63\x63\x65ss_key_secret\x18\x05 \x01(\t\x12\x13\n\x0bvirtualhost\x18\x06 \x01(\x08\x12\x0f\n\x07version\x18\x07 \x01(\t\x12\x14\n\x0cstorage_type\x18\x08 \x01(\t\"\\\n\x16\x44\x61tabaseDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x10\n\x08password\x18\x03 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x04 \x01(\t2\xd6\x06\n\x17\x44omainDataSourceService\x12\xa1\x01\n\x16\x43reateDomainDataSource\x12\x42.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainDataSourceRequest\x1a\x43.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainDataSourceResponse\x12\x9e\x01\n\x15QueryDomainDataSource\x12\x41.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainDataSourceRequest\x1a\x42.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainDataSourceResponse\x12\xa1\x01\n\x16UpdateDomainDataSource\x12\x42.kuscia.proto.api.v1alpha1.kusciaapi.UpdateDomainDataSourceRequest\x1a\x43.kuscia.proto.api.v1alpha1.kusciaapi.UpdateDomainDataSourceResponse\x12\xa1\x01\n\x16\x44\x65leteDomainDataSource\x12\x42.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainDataSourceRequest\x1a\x43.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainDataSourceResponse\x12\xad\x01\n\x1a\x42\x61tchQueryDomainDataSource\x12\x46.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainDataSourceRequest\x1aG.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainDataSourceResponseB^\n!org.secretflow.v1alpha1.kusciaapiZ9github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapib\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource.proto\x12#kuscia.proto.api.v1alpha1.kusciaapi\x1a&kuscia/proto/api/v1alpha1/common.proto\"\xd4\x02\n\x1d\x43reateDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x11\n\x04name\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x46\n\x04info\x18\x06 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DataSourceInfoH\x01\x88\x01\x01\x12\x15\n\x08info_key\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x1c\n\x0f\x61\x63\x63\x65ss_directly\x18\x08 \x01(\x08H\x03\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_infoB\x0b\n\t_info_keyB\x12\n\x10_access_directly\"\xaa\x01\n\x1e\x43reateDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12U\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32G.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainDataSourceResponseData\";\n\"CreateDomainDataSourceResponseData\x12\x15\n\rdatasource_id\x18\x01 \x01(\t\"\xd4\x02\n\x1dUpdateDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x11\n\x04name\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x46\n\x04info\x18\x06 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DataSourceInfoH\x01\x88\x01\x01\x12\x15\n\x08info_key\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x1c\n\x0f\x61\x63\x63\x65ss_directly\x18\x08 \x01(\x08H\x03\x88\x01\x01\x42\x07\n\x05_nameB\x07\n\x05_infoB\x0b\n\t_info_keyB\x12\n\x10_access_directly\"S\n\x1eUpdateDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"\x83\x01\n\x1d\x44\x65leteDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\"S\n\x1e\x44\x65leteDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\"\x82\x01\n\x1cQueryDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\x12\x15\n\rdatasource_id\x18\x03 \x01(\t\"\x97\x01\n\x1dQueryDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12\x43\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSource\"L\n QueryDomainDataSourceRequestData\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\x15\n\rdatasource_id\x18\x02 \x01(\t\"\xb2\x01\n!BatchQueryDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12S\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x45.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainDataSourceRequestData\"\xa0\x01\n\"BatchQueryDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12G\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x39.kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSourceList\"j\n\x1bListDomainDataSourceRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x11\n\tdomain_id\x18\x02 \x01(\t\"\x9a\x01\n\x1cListDomainDataSourceResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12G\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x39.kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSourceList\"f\n\x14\x44omainDataSourceList\x12N\n\x0f\x64\x61tasource_list\x18\x01 \x03(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSource\"\xd6\x01\n\x10\x44omainDataSource\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\x15\n\rdatasource_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\x12\x41\n\x04info\x18\x06 \x01(\x0b\x32\x33.kuscia.proto.api.v1alpha1.kusciaapi.DataSourceInfo\x12\x10\n\x08info_key\x18\x07 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65ss_directly\x18\x08 \x01(\x08\"\xef\x01\n\x0e\x44\x61taSourceInfo\x12I\n\x07localfs\x18\x01 \x01(\x0b\x32\x38.kuscia.proto.api.v1alpha1.kusciaapi.LocalDataSourceInfo\x12\x43\n\x03oss\x18\x02 \x01(\x0b\x32\x36.kuscia.proto.api.v1alpha1.kusciaapi.OssDataSourceInfo\x12M\n\x08\x64\x61tabase\x18\x03 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.kusciaapi.DatabaseDataSourceInfo\"#\n\x13LocalDataSourceInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\"\xb3\x01\n\x11OssDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0e\n\x06prefix\x18\x03 \x01(\t\x12\x15\n\raccess_key_id\x18\x04 \x01(\t\x12\x19\n\x11\x61\x63\x63\x65ss_key_secret\x18\x05 \x01(\t\x12\x13\n\x0bvirtualhost\x18\x06 \x01(\x08\x12\x0f\n\x07version\x18\x07 \x01(\t\x12\x14\n\x0cstorage_type\x18\x08 \x01(\t\"\\\n\x16\x44\x61tabaseDataSourceInfo\x12\x10\n\x08\x65ndpoint\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x10\n\x08password\x18\x03 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x04 \x01(\t2\xf4\x07\n\x17\x44omainDataSourceService\x12\xa1\x01\n\x16\x43reateDomainDataSource\x12\x42.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainDataSourceRequest\x1a\x43.kuscia.proto.api.v1alpha1.kusciaapi.CreateDomainDataSourceResponse\x12\x9e\x01\n\x15QueryDomainDataSource\x12\x41.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainDataSourceRequest\x1a\x42.kuscia.proto.api.v1alpha1.kusciaapi.QueryDomainDataSourceResponse\x12\xa1\x01\n\x16UpdateDomainDataSource\x12\x42.kuscia.proto.api.v1alpha1.kusciaapi.UpdateDomainDataSourceRequest\x1a\x43.kuscia.proto.api.v1alpha1.kusciaapi.UpdateDomainDataSourceResponse\x12\xa1\x01\n\x16\x44\x65leteDomainDataSource\x12\x42.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainDataSourceRequest\x1a\x43.kuscia.proto.api.v1alpha1.kusciaapi.DeleteDomainDataSourceResponse\x12\xad\x01\n\x1a\x42\x61tchQueryDomainDataSource\x12\x46.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainDataSourceRequest\x1aG.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryDomainDataSourceResponse\x12\x9b\x01\n\x14ListDomainDataSource\x12@.kuscia.proto.api.v1alpha1.kusciaapi.ListDomainDataSourceRequest\x1a\x41.kuscia.proto.api.v1alpha1.kusciaapi.ListDomainDataSourceResponseB^\n!org.secretflow.v1alpha1.kusciaapiZ9github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapib\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -47,18 +47,22 @@ _globals['_BATCHQUERYDOMAINDATASOURCEREQUEST']._serialized_end=1907 _globals['_BATCHQUERYDOMAINDATASOURCERESPONSE']._serialized_start=1910 _globals['_BATCHQUERYDOMAINDATASOURCERESPONSE']._serialized_end=2070 - _globals['_DOMAINDATASOURCELIST']._serialized_start=2072 - _globals['_DOMAINDATASOURCELIST']._serialized_end=2174 - _globals['_DOMAINDATASOURCE']._serialized_start=2177 - _globals['_DOMAINDATASOURCE']._serialized_end=2391 - _globals['_DATASOURCEINFO']._serialized_start=2394 - _globals['_DATASOURCEINFO']._serialized_end=2633 - _globals['_LOCALDATASOURCEINFO']._serialized_start=2635 - _globals['_LOCALDATASOURCEINFO']._serialized_end=2670 - _globals['_OSSDATASOURCEINFO']._serialized_start=2673 - _globals['_OSSDATASOURCEINFO']._serialized_end=2852 - _globals['_DATABASEDATASOURCEINFO']._serialized_start=2854 - _globals['_DATABASEDATASOURCEINFO']._serialized_end=2946 - _globals['_DOMAINDATASOURCESERVICE']._serialized_start=2949 - _globals['_DOMAINDATASOURCESERVICE']._serialized_end=3803 + _globals['_LISTDOMAINDATASOURCEREQUEST']._serialized_start=2072 + _globals['_LISTDOMAINDATASOURCEREQUEST']._serialized_end=2178 + _globals['_LISTDOMAINDATASOURCERESPONSE']._serialized_start=2181 + _globals['_LISTDOMAINDATASOURCERESPONSE']._serialized_end=2335 + _globals['_DOMAINDATASOURCELIST']._serialized_start=2337 + _globals['_DOMAINDATASOURCELIST']._serialized_end=2439 + _globals['_DOMAINDATASOURCE']._serialized_start=2442 + _globals['_DOMAINDATASOURCE']._serialized_end=2656 + _globals['_DATASOURCEINFO']._serialized_start=2659 + _globals['_DATASOURCEINFO']._serialized_end=2898 + _globals['_LOCALDATASOURCEINFO']._serialized_start=2900 + _globals['_LOCALDATASOURCEINFO']._serialized_end=2935 + _globals['_OSSDATASOURCEINFO']._serialized_start=2938 + _globals['_OSSDATASOURCEINFO']._serialized_end=3117 + _globals['_DATABASEDATASOURCEINFO']._serialized_start=3119 + _globals['_DATABASEDATASOURCEINFO']._serialized_end=3211 + _globals['_DOMAINDATASOURCESERVICE']._serialized_start=3214 + _globals['_DOMAINDATASOURCESERVICE']._serialized_end=4226 # @@protoc_insertion_point(module_scope) diff --git a/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2_grpc.py b/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2_grpc.py index f58d31b2..bcbb6899 100644 --- a/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2_grpc.py +++ b/python/kuscia/proto/api/v1alpha1/kusciaapi/domaindatasource_pb2_grpc.py @@ -39,6 +39,11 @@ def __init__(self, channel): request_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.BatchQueryDomainDataSourceRequest.SerializeToString, response_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.BatchQueryDomainDataSourceResponse.FromString, ) + self.ListDomainDataSource = channel.unary_unary( + '/kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSourceService/ListDomainDataSource', + request_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.ListDomainDataSourceRequest.SerializeToString, + response_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.ListDomainDataSourceResponse.FromString, + ) class DomainDataSourceServiceServicer(object): @@ -74,6 +79,12 @@ def BatchQueryDomainDataSource(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ListDomainDataSource(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_DomainDataSourceServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -102,6 +113,11 @@ def add_DomainDataSourceServiceServicer_to_server(servicer, server): request_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.BatchQueryDomainDataSourceRequest.FromString, response_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.BatchQueryDomainDataSourceResponse.SerializeToString, ), + 'ListDomainDataSource': grpc.unary_unary_rpc_method_handler( + servicer.ListDomainDataSource, + request_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.ListDomainDataSourceRequest.FromString, + response_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.ListDomainDataSourceResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSourceService', rpc_method_handlers) @@ -196,3 +212,20 @@ def BatchQueryDomainDataSource(request, kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.BatchQueryDomainDataSourceResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListDomainDataSource(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/kuscia.proto.api.v1alpha1.kusciaapi.DomainDataSourceService/ListDomainDataSource', + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.ListDomainDataSourceRequest.SerializeToString, + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_domaindatasource__pb2.ListDomainDataSourceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2.py b/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2.py index 4ff213bb..bf4a0f70 100644 --- a/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2.py +++ b/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2.py @@ -15,7 +15,7 @@ from kuscia.proto.api.v1alpha1 import common_pb2 as kuscia_dot_proto_dot_api_dot_v1alpha1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n-kuscia/proto/api/v1alpha1/kusciaapi/job.proto\x12#kuscia.proto.api.v1alpha1.kusciaapi\x1a&kuscia/proto/api/v1alpha1/common.proto\"\xd7\x02\n\x10\x43reateJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x11\n\tinitiator\x18\x03 \x01(\t\x12\x17\n\x0fmax_parallelism\x18\x04 \x01(\x05\x12\x38\n\x05tasks\x18\x05 \x03(\x0b\x32).kuscia.proto.api.v1alpha1.kusciaapi.Task\x12^\n\rcustom_fields\x18\x06 \x03(\x0b\x32G.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobRequest.CustomFieldsEntry\x1a\x33\n\x11\x43ustomFieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x90\x01\n\x11\x43reateJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12H\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobResponseData\"\'\n\x15\x43reateJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"\xb9\x01\n\x04Task\x12\x11\n\tapp_image\x18\x01 \x01(\t\x12;\n\x07parties\x18\x02 \x03(\x0b\x32*.kuscia.proto.api.v1alpha1.kusciaapi.Party\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x0f\n\x07task_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65pendencies\x18\x05 \x03(\t\x12\x19\n\x11task_input_config\x18\x06 \x01(\t\x12\x10\n\x08priority\x18\x07 \x01(\x05\"(\n\x05Party\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\x0c\n\x04role\x18\x02 \x01(\t\"\\\n\x10\x44\x65leteJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\"\x90\x01\n\x11\x44\x65leteJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12H\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.kusciaapi.DeleteJobResponseData\"\'\n\x15\x44\x65leteJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"Z\n\x0eStopJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\"\x8c\x01\n\x0fStopJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12\x46\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x38.kuscia.proto.api.v1alpha1.kusciaapi.StopJobResponseData\"%\n\x13StopJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"[\n\x0fQueryJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\"\x8e\x01\n\x10QueryJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12G\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x39.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobResponseData\"\xf1\x02\n\x14QueryJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x11\n\tinitiator\x18\x02 \x01(\t\x12\x17\n\x0fmax_parallelism\x18\x03 \x01(\x05\x12>\n\x05tasks\x18\x04 \x03(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.TaskConfig\x12\x44\n\x06status\x18\x05 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusDetail\x12\x62\n\rcustom_fields\x18\x06 \x03(\x0b\x32K.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobResponseData.CustomFieldsEntry\x1a\x33\n\x11\x43ustomFieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"w\n\x11\x41pproveJobRequest\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x42\n\x06result\x18\x02 \x01(\x0e\x32\x32.kuscia.proto.api.v1alpha1.kusciaapi.ApproveResult\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x92\x01\n\x12\x41pproveJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12I\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.kusciaapi.ApproveJobResponseData\"(\n\x16\x41pproveJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"\xd4\x02\n\x0fJobStatusDetail\x12\r\n\x05state\x18\x01 \x01(\t\x12\x0f\n\x07\x65rr_msg\x18\x02 \x01(\t\x12\x13\n\x0b\x63reate_time\x18\x03 \x01(\t\x12\x12\n\nstart_time\x18\x04 \x01(\t\x12\x10\n\x08\x65nd_time\x18\x05 \x01(\t\x12>\n\x05tasks\x18\x06 \x03(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.TaskStatus\x12P\n\x11stage_status_list\x18\x07 \x03(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.PartyStageStatus\x12T\n\x13\x61pprove_status_list\x18\x08 \x03(\x0b\x32\x37.kuscia.proto.api.v1alpha1.kusciaapi.PartyApproveStatus\"\xbf\x01\n\nTaskConfig\x12\x11\n\tapp_image\x18\x01 \x01(\t\x12;\n\x07parties\x18\x02 \x03(\x0b\x32*.kuscia.proto.api.v1alpha1.kusciaapi.Party\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x0f\n\x07task_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65pendencies\x18\x05 \x03(\t\x12\x19\n\x11task_input_config\x18\x06 \x01(\t\x12\x10\n\x08priority\x18\x07 \x01(\x05\"4\n\x10PartyStageStatus\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\"6\n\x12PartyApproveStatus\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\"\xbb\x01\n\nTaskStatus\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\x12\x0f\n\x07\x65rr_msg\x18\x03 \x01(\t\x12\x13\n\x0b\x63reate_time\x18\x04 \x01(\t\x12\x12\n\nstart_time\x18\x05 \x01(\t\x12\x10\n\x08\x65nd_time\x18\x06 \x01(\t\x12\x41\n\x07parties\x18\x07 \x03(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.PartyStatus\"\x8a\x01\n\x0bPartyStatus\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\x12\x0f\n\x07\x65rr_msg\x18\x03 \x01(\t\x12H\n\tendpoints\x18\x04 \x03(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.JobPartyEndpoint\"g\n\x1a\x42\x61tchQueryJobStatusRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0f\n\x07job_ids\x18\x02 \x03(\t\"\xa4\x01\n\x1b\x42\x61tchQueryJobStatusResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12R\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x44.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryJobStatusResponseData\"_\n\x1f\x42\x61tchQueryJobStatusResponseData\x12<\n\x04jobs\x18\x01 \x03(\x0b\x32..kuscia.proto.api.v1alpha1.kusciaapi.JobStatus\"\x90\x01\n\x11JobStatusResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12H\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusResponseData\"m\n\x15JobStatusResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x44\n\x06status\x18\x02 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusDetail\"a\n\tJobStatus\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x44\n\x06status\x18\x02 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusDetail\"d\n\x0fWatchJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x17\n\x0ftimeout_seconds\x18\x02 \x01(\x03\"\x95\x01\n\x15WatchJobEventResponse\x12<\n\x04type\x18\x01 \x01(\x0e\x32..kuscia.proto.api.v1alpha1.kusciaapi.EventType\x12>\n\x06object\x18\x02 \x01(\x0b\x32..kuscia.proto.api.v1alpha1.kusciaapi.JobStatus\"F\n\x10JobPartyEndpoint\x12\x11\n\tport_name\x18\x01 \x01(\t\x12\r\n\x05scope\x18\x02 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x03 \x01(\t*\x82\x01\n\x05State\x12\x0b\n\x07Unknown\x10\x00\x12\x0b\n\x07Pending\x10\x01\x12\x0b\n\x07Running\x10\x02\x12\r\n\tSucceeded\x10\x03\x12\n\n\x06\x46\x61iled\x10\x04\x12\x14\n\x10\x41waitingApproval\x10\x05\x12\x12\n\x0e\x41pprovalReject\x10\x06\x12\r\n\tCancelled\x10\x07*a\n\rApproveResult\x12\x1a\n\x16\x41PPROVE_RESULT_UNKNOWN\x10\x00\x12\x19\n\x15\x41PPROVE_RESULT_ACCEPT\x10\x01\x12\x19\n\x15\x41PPROVE_RESULT_REJECT\x10\x02*K\n\tEventType\x12\t\n\x05\x41\x44\x44\x45\x44\x10\x00\x12\x0c\n\x08MODIFIED\x10\x01\x12\x0b\n\x07\x44\x45LETED\x10\x02\x12\t\n\x05\x45RROR\x10\x03\x12\r\n\tHEARTBEAT\x10\x04\x32\x8d\x07\n\nJobService\x12z\n\tCreateJob\x12\x35.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobRequest\x1a\x36.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobResponse\x12w\n\x08QueryJob\x12\x34.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobRequest\x1a\x35.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobResponse\x12\x98\x01\n\x13\x42\x61tchQueryJobStatus\x12?.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryJobStatusRequest\x1a@.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryJobStatusResponse\x12t\n\x07StopJob\x12\x33.kuscia.proto.api.v1alpha1.kusciaapi.StopJobRequest\x1a\x34.kuscia.proto.api.v1alpha1.kusciaapi.StopJobResponse\x12z\n\tDeleteJob\x12\x35.kuscia.proto.api.v1alpha1.kusciaapi.DeleteJobRequest\x1a\x36.kuscia.proto.api.v1alpha1.kusciaapi.DeleteJobResponse\x12~\n\x08WatchJob\x12\x34.kuscia.proto.api.v1alpha1.kusciaapi.WatchJobRequest\x1a:.kuscia.proto.api.v1alpha1.kusciaapi.WatchJobEventResponse0\x01\x12}\n\nApproveJob\x12\x36.kuscia.proto.api.v1alpha1.kusciaapi.ApproveJobRequest\x1a\x37.kuscia.proto.api.v1alpha1.kusciaapi.ApproveJobResponseB^\n!org.secretflow.v1alpha1.kusciaapiZ9github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapib\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n-kuscia/proto/api/v1alpha1/kusciaapi/job.proto\x12#kuscia.proto.api.v1alpha1.kusciaapi\x1a&kuscia/proto/api/v1alpha1/common.proto\"\xd7\x02\n\x10\x43reateJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x11\n\tinitiator\x18\x03 \x01(\t\x12\x17\n\x0fmax_parallelism\x18\x04 \x01(\x05\x12\x38\n\x05tasks\x18\x05 \x03(\x0b\x32).kuscia.proto.api.v1alpha1.kusciaapi.Task\x12^\n\rcustom_fields\x18\x06 \x03(\x0b\x32G.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobRequest.CustomFieldsEntry\x1a\x33\n\x11\x43ustomFieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x90\x01\n\x11\x43reateJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12H\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobResponseData\"\'\n\x15\x43reateJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"\xb9\x01\n\x04Task\x12\x11\n\tapp_image\x18\x01 \x01(\t\x12;\n\x07parties\x18\x02 \x03(\x0b\x32*.kuscia.proto.api.v1alpha1.kusciaapi.Party\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x0f\n\x07task_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65pendencies\x18\x05 \x03(\t\x12\x19\n\x11task_input_config\x18\x06 \x01(\t\x12\x10\n\x08priority\x18\x07 \x01(\x05\"m\n\x05Party\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\x0c\n\x04role\x18\x02 \x01(\t\x12\x43\n\tresources\x18\x03 \x01(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.JobResource\"*\n\x0bJobResource\x12\x0b\n\x03\x63pu\x18\x01 \x01(\t\x12\x0e\n\x06memory\x18\x02 \x01(\t\"\\\n\x10\x44\x65leteJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\"\x90\x01\n\x11\x44\x65leteJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12H\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.kusciaapi.DeleteJobResponseData\"\'\n\x15\x44\x65leteJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"j\n\x0eStopJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x8c\x01\n\x0fStopJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12\x46\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x38.kuscia.proto.api.v1alpha1.kusciaapi.StopJobResponseData\"%\n\x13StopJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"m\n\x11SuspendJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x92\x01\n\x12SuspendJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12I\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.kusciaapi.SuspendJobResponseData\"(\n\x16SuspendJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"m\n\x11RestartJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x92\x01\n\x12RestartJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12I\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.kusciaapi.RestartJobResponseData\"(\n\x16RestartJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"l\n\x10\x43\x61ncelJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x90\x01\n\x11\x43\x61ncelJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12H\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.kusciaapi.CancelJobResponseData\"\'\n\x15\x43\x61ncelJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"[\n\x0fQueryJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0e\n\x06job_id\x18\x02 \x01(\t\"\x8e\x01\n\x10QueryJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12G\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x39.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobResponseData\"\xf1\x02\n\x14QueryJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x11\n\tinitiator\x18\x02 \x01(\t\x12\x17\n\x0fmax_parallelism\x18\x03 \x01(\x05\x12>\n\x05tasks\x18\x04 \x03(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.TaskConfig\x12\x44\n\x06status\x18\x05 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusDetail\x12\x62\n\rcustom_fields\x18\x06 \x03(\x0b\x32K.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobResponseData.CustomFieldsEntry\x1a\x33\n\x11\x43ustomFieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"w\n\x11\x41pproveJobRequest\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x42\n\x06result\x18\x02 \x01(\x0e\x32\x32.kuscia.proto.api.v1alpha1.kusciaapi.ApproveResult\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x92\x01\n\x12\x41pproveJobResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12I\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32;.kuscia.proto.api.v1alpha1.kusciaapi.ApproveJobResponseData\"(\n\x16\x41pproveJobResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"\xd4\x02\n\x0fJobStatusDetail\x12\r\n\x05state\x18\x01 \x01(\t\x12\x0f\n\x07\x65rr_msg\x18\x02 \x01(\t\x12\x13\n\x0b\x63reate_time\x18\x03 \x01(\t\x12\x12\n\nstart_time\x18\x04 \x01(\t\x12\x10\n\x08\x65nd_time\x18\x05 \x01(\t\x12>\n\x05tasks\x18\x06 \x03(\x0b\x32/.kuscia.proto.api.v1alpha1.kusciaapi.TaskStatus\x12P\n\x11stage_status_list\x18\x07 \x03(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.PartyStageStatus\x12T\n\x13\x61pprove_status_list\x18\x08 \x03(\x0b\x32\x37.kuscia.proto.api.v1alpha1.kusciaapi.PartyApproveStatus\"\xbf\x01\n\nTaskConfig\x12\x11\n\tapp_image\x18\x01 \x01(\t\x12;\n\x07parties\x18\x02 \x03(\x0b\x32*.kuscia.proto.api.v1alpha1.kusciaapi.Party\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x0f\n\x07task_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65pendencies\x18\x05 \x03(\t\x12\x19\n\x11task_input_config\x18\x06 \x01(\t\x12\x10\n\x08priority\x18\x07 \x01(\x05\"4\n\x10PartyStageStatus\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\"6\n\x12PartyApproveStatus\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\"\xca\x01\n\nTaskStatus\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\x12\x0f\n\x07\x65rr_msg\x18\x03 \x01(\t\x12\x13\n\x0b\x63reate_time\x18\x04 \x01(\t\x12\x12\n\nstart_time\x18\x05 \x01(\t\x12\x10\n\x08\x65nd_time\x18\x06 \x01(\t\x12\x41\n\x07parties\x18\x07 \x03(\x0b\x32\x30.kuscia.proto.api.v1alpha1.kusciaapi.PartyStatus\x12\r\n\x05\x61lias\x18\x08 \x01(\t\"\x8a\x01\n\x0bPartyStatus\x12\x11\n\tdomain_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\x12\x0f\n\x07\x65rr_msg\x18\x03 \x01(\t\x12H\n\tendpoints\x18\x04 \x03(\x0b\x32\x35.kuscia.proto.api.v1alpha1.kusciaapi.JobPartyEndpoint\"g\n\x1a\x42\x61tchQueryJobStatusRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x0f\n\x07job_ids\x18\x02 \x03(\t\"\xa4\x01\n\x1b\x42\x61tchQueryJobStatusResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12R\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x44.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryJobStatusResponseData\"_\n\x1f\x42\x61tchQueryJobStatusResponseData\x12<\n\x04jobs\x18\x01 \x03(\x0b\x32..kuscia.proto.api.v1alpha1.kusciaapi.JobStatus\"\x90\x01\n\x11JobStatusResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.kuscia.proto.api.v1alpha1.Status\x12H\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32:.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusResponseData\"m\n\x15JobStatusResponseData\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x44\n\x06status\x18\x02 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusDetail\"a\n\tJobStatus\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x44\n\x06status\x18\x02 \x01(\x0b\x32\x34.kuscia.proto.api.v1alpha1.kusciaapi.JobStatusDetail\"d\n\x0fWatchJobRequest\x12\x38\n\x06header\x18\x01 \x01(\x0b\x32(.kuscia.proto.api.v1alpha1.RequestHeader\x12\x17\n\x0ftimeout_seconds\x18\x02 \x01(\x03\"\x95\x01\n\x15WatchJobEventResponse\x12<\n\x04type\x18\x01 \x01(\x0e\x32..kuscia.proto.api.v1alpha1.kusciaapi.EventType\x12>\n\x06object\x18\x02 \x01(\x0b\x32..kuscia.proto.api.v1alpha1.kusciaapi.JobStatus\"F\n\x10JobPartyEndpoint\x12\x11\n\tport_name\x18\x01 \x01(\t\x12\r\n\x05scope\x18\x02 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x03 \x01(\t*\x91\x01\n\x05State\x12\x0b\n\x07Unknown\x10\x00\x12\x0b\n\x07Pending\x10\x01\x12\x0b\n\x07Running\x10\x02\x12\r\n\tSucceeded\x10\x03\x12\n\n\x06\x46\x61iled\x10\x04\x12\x14\n\x10\x41waitingApproval\x10\x05\x12\x12\n\x0e\x41pprovalReject\x10\x06\x12\r\n\tCancelled\x10\x07\x12\r\n\tSuspended\x10\x08*a\n\rApproveResult\x12\x1a\n\x16\x41PPROVE_RESULT_UNKNOWN\x10\x00\x12\x19\n\x15\x41PPROVE_RESULT_ACCEPT\x10\x01\x12\x19\n\x15\x41PPROVE_RESULT_REJECT\x10\x02*K\n\tEventType\x12\t\n\x05\x41\x44\x44\x45\x44\x10\x00\x12\x0c\n\x08MODIFIED\x10\x01\x12\x0b\n\x07\x44\x45LETED\x10\x02\x12\t\n\x05\x45RROR\x10\x03\x12\r\n\tHEARTBEAT\x10\x04\x32\x87\n\n\nJobService\x12z\n\tCreateJob\x12\x35.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobRequest\x1a\x36.kuscia.proto.api.v1alpha1.kusciaapi.CreateJobResponse\x12w\n\x08QueryJob\x12\x34.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobRequest\x1a\x35.kuscia.proto.api.v1alpha1.kusciaapi.QueryJobResponse\x12\x98\x01\n\x13\x42\x61tchQueryJobStatus\x12?.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryJobStatusRequest\x1a@.kuscia.proto.api.v1alpha1.kusciaapi.BatchQueryJobStatusResponse\x12t\n\x07StopJob\x12\x33.kuscia.proto.api.v1alpha1.kusciaapi.StopJobRequest\x1a\x34.kuscia.proto.api.v1alpha1.kusciaapi.StopJobResponse\x12}\n\nRestartJob\x12\x36.kuscia.proto.api.v1alpha1.kusciaapi.RestartJobRequest\x1a\x37.kuscia.proto.api.v1alpha1.kusciaapi.RestartJobResponse\x12}\n\nSuspendJob\x12\x36.kuscia.proto.api.v1alpha1.kusciaapi.SuspendJobRequest\x1a\x37.kuscia.proto.api.v1alpha1.kusciaapi.SuspendJobResponse\x12z\n\tCancelJob\x12\x35.kuscia.proto.api.v1alpha1.kusciaapi.CancelJobRequest\x1a\x36.kuscia.proto.api.v1alpha1.kusciaapi.CancelJobResponse\x12z\n\tDeleteJob\x12\x35.kuscia.proto.api.v1alpha1.kusciaapi.DeleteJobRequest\x1a\x36.kuscia.proto.api.v1alpha1.kusciaapi.DeleteJobResponse\x12~\n\x08WatchJob\x12\x34.kuscia.proto.api.v1alpha1.kusciaapi.WatchJobRequest\x1a:.kuscia.proto.api.v1alpha1.kusciaapi.WatchJobEventResponse0\x01\x12}\n\nApproveJob\x12\x36.kuscia.proto.api.v1alpha1.kusciaapi.ApproveJobRequest\x1a\x37.kuscia.proto.api.v1alpha1.kusciaapi.ApproveJobResponseB^\n!org.secretflow.v1alpha1.kusciaapiZ9github.com/secretflow/kuscia/proto/api/v1alpha1/kusciaapib\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -27,12 +27,12 @@ _globals['_CREATEJOBREQUEST_CUSTOMFIELDSENTRY']._serialized_options = b'8\001' _globals['_QUERYJOBRESPONSEDATA_CUSTOMFIELDSENTRY']._options = None _globals['_QUERYJOBRESPONSEDATA_CUSTOMFIELDSENTRY']._serialized_options = b'8\001' - _globals['_STATE']._serialized_start=4399 - _globals['_STATE']._serialized_end=4529 - _globals['_APPROVERESULT']._serialized_start=4531 - _globals['_APPROVERESULT']._serialized_end=4628 - _globals['_EVENTTYPE']._serialized_start=4630 - _globals['_EVENTTYPE']._serialized_end=4705 + _globals['_STATE']._serialized_start=5445 + _globals['_STATE']._serialized_end=5590 + _globals['_APPROVERESULT']._serialized_start=5592 + _globals['_APPROVERESULT']._serialized_end=5689 + _globals['_EVENTTYPE']._serialized_start=5691 + _globals['_EVENTTYPE']._serialized_end=5766 _globals['_CREATEJOBREQUEST']._serialized_start=127 _globals['_CREATEJOBREQUEST']._serialized_end=470 _globals['_CREATEJOBREQUEST_CUSTOMFIELDSENTRY']._serialized_start=419 @@ -44,63 +44,83 @@ _globals['_TASK']._serialized_start=661 _globals['_TASK']._serialized_end=846 _globals['_PARTY']._serialized_start=848 - _globals['_PARTY']._serialized_end=888 - _globals['_DELETEJOBREQUEST']._serialized_start=890 - _globals['_DELETEJOBREQUEST']._serialized_end=982 - _globals['_DELETEJOBRESPONSE']._serialized_start=985 - _globals['_DELETEJOBRESPONSE']._serialized_end=1129 - _globals['_DELETEJOBRESPONSEDATA']._serialized_start=1131 - _globals['_DELETEJOBRESPONSEDATA']._serialized_end=1170 - _globals['_STOPJOBREQUEST']._serialized_start=1172 - _globals['_STOPJOBREQUEST']._serialized_end=1262 - _globals['_STOPJOBRESPONSE']._serialized_start=1265 - _globals['_STOPJOBRESPONSE']._serialized_end=1405 - _globals['_STOPJOBRESPONSEDATA']._serialized_start=1407 - _globals['_STOPJOBRESPONSEDATA']._serialized_end=1444 - _globals['_QUERYJOBREQUEST']._serialized_start=1446 - _globals['_QUERYJOBREQUEST']._serialized_end=1537 - _globals['_QUERYJOBRESPONSE']._serialized_start=1540 - _globals['_QUERYJOBRESPONSE']._serialized_end=1682 - _globals['_QUERYJOBRESPONSEDATA']._serialized_start=1685 - _globals['_QUERYJOBRESPONSEDATA']._serialized_end=2054 + _globals['_PARTY']._serialized_end=957 + _globals['_JOBRESOURCE']._serialized_start=959 + _globals['_JOBRESOURCE']._serialized_end=1001 + _globals['_DELETEJOBREQUEST']._serialized_start=1003 + _globals['_DELETEJOBREQUEST']._serialized_end=1095 + _globals['_DELETEJOBRESPONSE']._serialized_start=1098 + _globals['_DELETEJOBRESPONSE']._serialized_end=1242 + _globals['_DELETEJOBRESPONSEDATA']._serialized_start=1244 + _globals['_DELETEJOBRESPONSEDATA']._serialized_end=1283 + _globals['_STOPJOBREQUEST']._serialized_start=1285 + _globals['_STOPJOBREQUEST']._serialized_end=1391 + _globals['_STOPJOBRESPONSE']._serialized_start=1394 + _globals['_STOPJOBRESPONSE']._serialized_end=1534 + _globals['_STOPJOBRESPONSEDATA']._serialized_start=1536 + _globals['_STOPJOBRESPONSEDATA']._serialized_end=1573 + _globals['_SUSPENDJOBREQUEST']._serialized_start=1575 + _globals['_SUSPENDJOBREQUEST']._serialized_end=1684 + _globals['_SUSPENDJOBRESPONSE']._serialized_start=1687 + _globals['_SUSPENDJOBRESPONSE']._serialized_end=1833 + _globals['_SUSPENDJOBRESPONSEDATA']._serialized_start=1835 + _globals['_SUSPENDJOBRESPONSEDATA']._serialized_end=1875 + _globals['_RESTARTJOBREQUEST']._serialized_start=1877 + _globals['_RESTARTJOBREQUEST']._serialized_end=1986 + _globals['_RESTARTJOBRESPONSE']._serialized_start=1989 + _globals['_RESTARTJOBRESPONSE']._serialized_end=2135 + _globals['_RESTARTJOBRESPONSEDATA']._serialized_start=2137 + _globals['_RESTARTJOBRESPONSEDATA']._serialized_end=2177 + _globals['_CANCELJOBREQUEST']._serialized_start=2179 + _globals['_CANCELJOBREQUEST']._serialized_end=2287 + _globals['_CANCELJOBRESPONSE']._serialized_start=2290 + _globals['_CANCELJOBRESPONSE']._serialized_end=2434 + _globals['_CANCELJOBRESPONSEDATA']._serialized_start=2436 + _globals['_CANCELJOBRESPONSEDATA']._serialized_end=2475 + _globals['_QUERYJOBREQUEST']._serialized_start=2477 + _globals['_QUERYJOBREQUEST']._serialized_end=2568 + _globals['_QUERYJOBRESPONSE']._serialized_start=2571 + _globals['_QUERYJOBRESPONSE']._serialized_end=2713 + _globals['_QUERYJOBRESPONSEDATA']._serialized_start=2716 + _globals['_QUERYJOBRESPONSEDATA']._serialized_end=3085 _globals['_QUERYJOBRESPONSEDATA_CUSTOMFIELDSENTRY']._serialized_start=419 _globals['_QUERYJOBRESPONSEDATA_CUSTOMFIELDSENTRY']._serialized_end=470 - _globals['_APPROVEJOBREQUEST']._serialized_start=2056 - _globals['_APPROVEJOBREQUEST']._serialized_end=2175 - _globals['_APPROVEJOBRESPONSE']._serialized_start=2178 - _globals['_APPROVEJOBRESPONSE']._serialized_end=2324 - _globals['_APPROVEJOBRESPONSEDATA']._serialized_start=2326 - _globals['_APPROVEJOBRESPONSEDATA']._serialized_end=2366 - _globals['_JOBSTATUSDETAIL']._serialized_start=2369 - _globals['_JOBSTATUSDETAIL']._serialized_end=2709 - _globals['_TASKCONFIG']._serialized_start=2712 - _globals['_TASKCONFIG']._serialized_end=2903 - _globals['_PARTYSTAGESTATUS']._serialized_start=2905 - _globals['_PARTYSTAGESTATUS']._serialized_end=2957 - _globals['_PARTYAPPROVESTATUS']._serialized_start=2959 - _globals['_PARTYAPPROVESTATUS']._serialized_end=3013 - _globals['_TASKSTATUS']._serialized_start=3016 - _globals['_TASKSTATUS']._serialized_end=3203 - _globals['_PARTYSTATUS']._serialized_start=3206 - _globals['_PARTYSTATUS']._serialized_end=3344 - _globals['_BATCHQUERYJOBSTATUSREQUEST']._serialized_start=3346 - _globals['_BATCHQUERYJOBSTATUSREQUEST']._serialized_end=3449 - _globals['_BATCHQUERYJOBSTATUSRESPONSE']._serialized_start=3452 - _globals['_BATCHQUERYJOBSTATUSRESPONSE']._serialized_end=3616 - _globals['_BATCHQUERYJOBSTATUSRESPONSEDATA']._serialized_start=3618 - _globals['_BATCHQUERYJOBSTATUSRESPONSEDATA']._serialized_end=3713 - _globals['_JOBSTATUSRESPONSE']._serialized_start=3716 - _globals['_JOBSTATUSRESPONSE']._serialized_end=3860 - _globals['_JOBSTATUSRESPONSEDATA']._serialized_start=3862 - _globals['_JOBSTATUSRESPONSEDATA']._serialized_end=3971 - _globals['_JOBSTATUS']._serialized_start=3973 - _globals['_JOBSTATUS']._serialized_end=4070 - _globals['_WATCHJOBREQUEST']._serialized_start=4072 - _globals['_WATCHJOBREQUEST']._serialized_end=4172 - _globals['_WATCHJOBEVENTRESPONSE']._serialized_start=4175 - _globals['_WATCHJOBEVENTRESPONSE']._serialized_end=4324 - _globals['_JOBPARTYENDPOINT']._serialized_start=4326 - _globals['_JOBPARTYENDPOINT']._serialized_end=4396 - _globals['_JOBSERVICE']._serialized_start=4708 - _globals['_JOBSERVICE']._serialized_end=5617 + _globals['_APPROVEJOBREQUEST']._serialized_start=3087 + _globals['_APPROVEJOBREQUEST']._serialized_end=3206 + _globals['_APPROVEJOBRESPONSE']._serialized_start=3209 + _globals['_APPROVEJOBRESPONSE']._serialized_end=3355 + _globals['_APPROVEJOBRESPONSEDATA']._serialized_start=3357 + _globals['_APPROVEJOBRESPONSEDATA']._serialized_end=3397 + _globals['_JOBSTATUSDETAIL']._serialized_start=3400 + _globals['_JOBSTATUSDETAIL']._serialized_end=3740 + _globals['_TASKCONFIG']._serialized_start=3743 + _globals['_TASKCONFIG']._serialized_end=3934 + _globals['_PARTYSTAGESTATUS']._serialized_start=3936 + _globals['_PARTYSTAGESTATUS']._serialized_end=3988 + _globals['_PARTYAPPROVESTATUS']._serialized_start=3990 + _globals['_PARTYAPPROVESTATUS']._serialized_end=4044 + _globals['_TASKSTATUS']._serialized_start=4047 + _globals['_TASKSTATUS']._serialized_end=4249 + _globals['_PARTYSTATUS']._serialized_start=4252 + _globals['_PARTYSTATUS']._serialized_end=4390 + _globals['_BATCHQUERYJOBSTATUSREQUEST']._serialized_start=4392 + _globals['_BATCHQUERYJOBSTATUSREQUEST']._serialized_end=4495 + _globals['_BATCHQUERYJOBSTATUSRESPONSE']._serialized_start=4498 + _globals['_BATCHQUERYJOBSTATUSRESPONSE']._serialized_end=4662 + _globals['_BATCHQUERYJOBSTATUSRESPONSEDATA']._serialized_start=4664 + _globals['_BATCHQUERYJOBSTATUSRESPONSEDATA']._serialized_end=4759 + _globals['_JOBSTATUSRESPONSE']._serialized_start=4762 + _globals['_JOBSTATUSRESPONSE']._serialized_end=4906 + _globals['_JOBSTATUSRESPONSEDATA']._serialized_start=4908 + _globals['_JOBSTATUSRESPONSEDATA']._serialized_end=5017 + _globals['_JOBSTATUS']._serialized_start=5019 + _globals['_JOBSTATUS']._serialized_end=5116 + _globals['_WATCHJOBREQUEST']._serialized_start=5118 + _globals['_WATCHJOBREQUEST']._serialized_end=5218 + _globals['_WATCHJOBEVENTRESPONSE']._serialized_start=5221 + _globals['_WATCHJOBEVENTRESPONSE']._serialized_end=5370 + _globals['_JOBPARTYENDPOINT']._serialized_start=5372 + _globals['_JOBPARTYENDPOINT']._serialized_end=5442 + _globals['_JOBSERVICE']._serialized_start=5769 + _globals['_JOBSERVICE']._serialized_end=7056 # @@protoc_insertion_point(module_scope) diff --git a/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2_grpc.py b/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2_grpc.py index 37c2627c..d810f2bf 100644 --- a/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2_grpc.py +++ b/python/kuscia/proto/api/v1alpha1/kusciaapi/job_pb2_grpc.py @@ -34,6 +34,21 @@ def __init__(self, channel): request_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.StopJobRequest.SerializeToString, response_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.StopJobResponse.FromString, ) + self.RestartJob = channel.unary_unary( + '/kuscia.proto.api.v1alpha1.kusciaapi.JobService/RestartJob', + request_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.RestartJobRequest.SerializeToString, + response_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.RestartJobResponse.FromString, + ) + self.SuspendJob = channel.unary_unary( + '/kuscia.proto.api.v1alpha1.kusciaapi.JobService/SuspendJob', + request_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.SuspendJobRequest.SerializeToString, + response_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.SuspendJobResponse.FromString, + ) + self.CancelJob = channel.unary_unary( + '/kuscia.proto.api.v1alpha1.kusciaapi.JobService/CancelJob', + request_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.CancelJobRequest.SerializeToString, + response_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.CancelJobResponse.FromString, + ) self.DeleteJob = channel.unary_unary( '/kuscia.proto.api.v1alpha1.kusciaapi.JobService/DeleteJob', request_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.DeleteJobRequest.SerializeToString, @@ -78,6 +93,24 @@ def StopJob(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def RestartJob(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SuspendJob(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CancelJob(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def DeleteJob(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -119,6 +152,21 @@ def add_JobServiceServicer_to_server(servicer, server): request_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.StopJobRequest.FromString, response_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.StopJobResponse.SerializeToString, ), + 'RestartJob': grpc.unary_unary_rpc_method_handler( + servicer.RestartJob, + request_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.RestartJobRequest.FromString, + response_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.RestartJobResponse.SerializeToString, + ), + 'SuspendJob': grpc.unary_unary_rpc_method_handler( + servicer.SuspendJob, + request_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.SuspendJobRequest.FromString, + response_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.SuspendJobResponse.SerializeToString, + ), + 'CancelJob': grpc.unary_unary_rpc_method_handler( + servicer.CancelJob, + request_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.CancelJobRequest.FromString, + response_serializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.CancelJobResponse.SerializeToString, + ), 'DeleteJob': grpc.unary_unary_rpc_method_handler( servicer.DeleteJob, request_deserializer=kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.DeleteJobRequest.FromString, @@ -212,6 +260,57 @@ def StopJob(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def RestartJob(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/kuscia.proto.api.v1alpha1.kusciaapi.JobService/RestartJob', + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.RestartJobRequest.SerializeToString, + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.RestartJobResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SuspendJob(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/kuscia.proto.api.v1alpha1.kusciaapi.JobService/SuspendJob', + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.SuspendJobRequest.SerializeToString, + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.SuspendJobResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def CancelJob(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/kuscia.proto.api.v1alpha1.kusciaapi.JobService/CancelJob', + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.CancelJobRequest.SerializeToString, + kuscia_dot_proto_dot_api_dot_v1alpha1_dot_kusciaapi_dot_job__pb2.CancelJobResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def DeleteJob(request, target, diff --git a/python/requirements.txt b/python/requirements.txt index 886ba1b2..cc17cf24 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,3 +1,3 @@ protobuf>=4,<5 -grpcio>=1.42.0,!=1.48.0 -grpcio-tools +grpcio==1.62.2 +grpcio-tools==1.62.2 diff --git a/python/test/test_datamesh.py b/python/test/test_datamesh.py new file mode 100644 index 00000000..a3436a77 --- /dev/null +++ b/python/test/test_datamesh.py @@ -0,0 +1,145 @@ +# Copyright 2024 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import sys +sys.path.append('..') + +import os +import logging +import hashlib +from datetime import datetime +import kuscia.datamesh as datamesh +import kuscia.datamesh.api as dmapi + +oss_datasource_id = "oss-data-source" +local_datasource_id = "default-data-source" + +local_dir = "/tmp/datamesh/var/client" + +def get_file_sha256(filepath): + with open(filepath, "r") as file: + content = file.read() + return hashlib.sha256(content.encode()).hexdigest() + +def new_file_name(ext): + return 'test_' + datetime.now().strftime("%Y%m%d_%H%M%S") + ext + +def new_column(name, type): + col1 = dmapi.DataColumn() + col1.name = name + col1.type = type + return col1 + +def init_binary_domaindata(datasource_id, filepath): + new_domain_data = dmapi.DomainData() + new_domain_data.name = "test" + new_domain_data.type = "raw" + new_domain_data.relative_uri = filepath + new_domain_data.datasource_id = datasource_id + new_domain_data.vendor = "test" + return new_domain_data + +def init_csv_domaindata(datasource_id, filepath): + new_domain_data = dmapi.DomainData() + new_domain_data.name = "test" + new_domain_data.type = "table" + new_domain_data.relative_uri = filepath + new_domain_data.datasource_id = datasource_id + new_domain_data.vendor = "test" + new_domain_data.columns.append(new_column('ID', 'string')) + new_domain_data.columns.append(new_column('credit_rank', 'int32')) + new_domain_data.columns.append(new_column('income', 'int32')) + new_domain_data.columns.append(new_column('age', 'int32')) + return new_domain_data + +def test_binary_file_upload(datasource_id, local_dir, filename): + new_domain_data = init_binary_domaindata(datasource_id, new_file_name(".raw")) + + domaindata_id = datamesh.create_domaindata(new_domain_data, dmapi.FileFormat.BINARY) + print("created domaindata: ", domaindata_id) + + datamesh.upload_file(domaindata_id, os.path.join(local_dir, filename), dmapi.FileFormat.BINARY) + + +def test_binary_file_download(datasource_id, local_dir): + new_domain_data = init_binary_domaindata(datasource_id, "test.raw") + + domaindata_id = datamesh.create_domaindata(new_domain_data, dmapi.FileFormat.BINARY) + print("created domaindata: ", domaindata_id) + + download_path = os.path.join(local_dir, "download-"+domaindata_id+".raw") + datamesh.download_to_file(domaindata_id, download_path, dmapi.FileFormat.BINARY) + print("download to file: ", download_path) + print("[OUTPUT] file sha256=", get_file_sha256(download_path)) + + with open(download_path, "r") as file: + content = file.read() + print("FILE-CONTENT: ") + print(content) + +def test_csv_file_download(datasource_id, local_dir): + new_domain_data = init_csv_domaindata(datasource_id, "alice.csv") + + domaindata_id = datamesh.create_domaindata(new_domain_data, dmapi.FileFormat.CSV) + print("created domaindata: ", domaindata_id) + + download_path = os.path.join(local_dir, "download-"+domaindata_id+".csv") + datamesh.download_to_file(domaindata_id, download_path, dmapi.FileFormat.CSV) + print("download to file: ", download_path) + print("[OUTPUT] file sha256=", get_file_sha256(download_path)) + + with open(download_path, "r") as file: + content = file.read() + print("FILE-CONTENT: ") + print(content) + +def test_csv_file_upload(datasource_id, local_dir, filename): + new_domain_data = init_csv_domaindata(datasource_id, new_file_name(".csv")) + + domaindata_id = datamesh.create_domaindata(new_domain_data, dmapi.FileFormat.CSV) + print("created domaindata: ", domaindata_id) + + datamesh.upload_file(domaindata_id, os.path.join(local_dir, filename), dmapi.FileFormat.CSV) + +if not os.path.exists(local_dir): + os.makedirs(local_dir) +with open(os.path.join(local_dir, "local-test.csv"), "w") as file: + file.write("hello world!") + +with open(os.path.join(local_dir, "large-test.csv"), "w") as file: + for i in range(500 * 1024): + file.write("hello world!\n") + +logging.root.setLevel(logging.DEBUG) + + +datamesh.init("127.0.0.1:8071") + + +print("[INPUT] init file sha256=", get_file_sha256(os.path.join(local_dir, "local-test.csv"))) + +#test_binary_file_upload(local_datasource_id, local_dir) + +#test_binary_file_download(local_datasource_id, local_dir) + + +#test_binary_file_upload(oss_datasource_id, local_dir, "local-test.csv") +#test_binary_file_upload(oss_datasource_id, local_dir, "large-test.csv") + +#test_binary_file_download(local_datasource_id, local_dir) + + +#test_csv_file_download(local_datasource_id, local_dir) +#test_csv_file_upload(local_datasource_id, local_dir, 'alice.csv') \ No newline at end of file diff --git a/scripts/deploy/create_reverse_tunnel_test_cluster.sh b/scripts/deploy/create_reverse_tunnel_test_cluster.sh index 01eed0ba..b0458dff 100644 --- a/scripts/deploy/create_reverse_tunnel_test_cluster.sh +++ b/scripts/deploy/create_reverse_tunnel_test_cluster.sh @@ -88,12 +88,12 @@ function create_kuscia_yaml() { if [ ! -d alice ]; then mkdir alice fi - docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode autonomy --domain "alice" --runtime "runp" --log-level "DEBUG" --datastore-endpoint "mysql://root:password@tcp($hostname:13307)/kine" > alice/kuscia.yaml + docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode autonomy --domain "alice" --runtime "runp" --log-level "DEBUG" --datastore-endpoint "mysql://root:password@tcp($hostname:13307)/kine" > alice/kuscia.yaml 2>&1 || cat alice/kuscia.yaml if [ ! -d bob ]; then mkdir bob fi - docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode autonomy --domain "bob" --runtime "runp" --log-level "DEBUG" --datastore-endpoint "mysql://root:password@tcp($hostname:13308)/kine" > bob/kuscia.yaml + docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode autonomy --domain "bob" --runtime "runp" --log-level "DEBUG" --datastore-endpoint "mysql://root:password@tcp($hostname:13308)/kine" > bob/kuscia.yaml 2>&1 || cat bob/kuscia.yaml } function create_load() { diff --git a/scripts/deploy/deploy.sh b/scripts/deploy/deploy.sh index 97c4fb85..db698449 100755 --- a/scripts/deploy/deploy.sh +++ b/scripts/deploy/deploy.sh @@ -308,7 +308,7 @@ function deploy_autonomy() { if [[ ${KUSCIA_CONFIG_FILE} == "" ]]; then mkdir -p "${conf_dir}" - docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode Autonomy --domain "${DOMAIN_ID}" >"${kuscia_config_file}" + docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode Autonomy --domain "${DOMAIN_ID}" >"${kuscia_config_file}" 2>&1 || cat "${kuscia_config_file}" wrap_kuscia_config_file "${kuscia_config_file}" fi @@ -330,7 +330,7 @@ function deploy_autonomy() { --env NAMESPACE=${DOMAIN_ID} \ "${KUSCIA_IMAGE}" bin/kuscia start -c etc/conf/kuscia.yaml - probe_gateway_crd "${domain_ctr}" "${DOMAIN_ID}" "${domain_ctr}" 60 + probe_datamesh "${domain_ctr}" docker exec -it "${domain_ctr}" sh scripts/deploy/init_kusciaapi_client_certs.sh log "Container ${domain_ctr} started successfully" @@ -376,7 +376,7 @@ function deploy_lite() { if [[ ${KUSCIA_CONFIG_FILE} == "" ]]; then mkdir -p "${conf_dir}" - docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode Lite --domain "${DOMAIN_ID}" --master-endpoint "${MASTER_ENDPOINT}" --lite-deploy-token "${DOMAIN_TOKEN}" >"${kuscia_config_file}" + docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode Lite --domain "${DOMAIN_ID}" --master-endpoint "${MASTER_ENDPOINT}" --lite-deploy-token "${DOMAIN_TOKEN}" >"${kuscia_config_file}" 2>&1 || cat "${kuscia_config_file}" wrap_kuscia_config_file "${kuscia_config_file}" fi @@ -429,7 +429,7 @@ function deploy_master() { if [[ ${KUSCIA_CONFIG_FILE} == "" ]]; then mkdir -p "${conf_dir}" - docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode Master --domain "$master_domain_id" >"${kuscia_config_file}" + docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode Master --domain "$master_domain_id" >"${kuscia_config_file}" 2>&1 || cat "${kuscia_config_file}" fi docker run -dit --name="${domain_ctr}" --hostname="${domain_ctr}" --restart=always --network=${NETWORK_NAME} -m ${MASTER_MEMORY_LIMIT} \ diff --git a/scripts/deploy/import_engine_image.sh b/scripts/deploy/import_engine_image.sh deleted file mode 100644 index 7afc45ff..00000000 --- a/scripts/deploy/import_engine_image.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# -# Copyright 2023 Ant Group Co., Ltd. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -set -e - -GREEN='\033[0;32m' -NC='\033[0m' -RED='\033[31m' - -kuscia_container_name=$1 -engine_image=$2 - -if docker exec -it ${kuscia_container_name} crictl inspecti ${engine_image} >/dev/null 2>&1; then - echo -e "${GREEN}Image '${engine_image}' already exists in domain ${kuscia_container_name}${NC}" -else - if docker image inspect ${engine_image} >/dev/null 2>&1; then - echo -e "${GREEN}Found the engine image '${engine_image}' on host${NC}" - else - echo -e "${GREEN}Not found the engine image '${engine_image}' on host${NC}" - echo -e "${GREEN}Start pulling image '${engine_image}' ...${NC}" - docker pull ${engine_image} - fi - image_tag=$(echo ${engine_image} | cut -d ':' -f 2) - echo -e "${GREEN}Start importing image '${engine_image}' Please be patient...${NC}" - - image_tar=/tmp/${image_tag}.tar - docker save ${engine_image} -o ${image_tar} - docker exec -it ${kuscia_container_name} ctr -a=/home/kuscia/containerd/run/containerd.sock -n=k8s.io images import ${image_tar} - - if docker exec -it ${kuscia_container_name} crictl inspecti ${engine_image} >/dev/null 2>&1; then - rm -rf ${image_tar} - echo -e "${GREEN}image ${engine_image} import successfully${NC}" - else - echo -e "${RED}error: ${engine_image} import failed${NC}" - fi -fi \ No newline at end of file diff --git a/scripts/deploy/init_kusciaapi_client_certs.sh b/scripts/deploy/init_kusciaapi_client_certs.sh index 8e3e73a7..e7a65cd9 100755 --- a/scripts/deploy/init_kusciaapi_client_certs.sh +++ b/scripts/deploy/init_kusciaapi_client_certs.sh @@ -24,15 +24,15 @@ pushd ${ROOT}/var/certs >/dev/null || exit CLIENT=kusciaapi-client #create a PKCS#8 key for client(JAVA native supported), default is PKCS#1 -openssl genpkey -out ${CLIENT}.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048 >/dev/null 2>&1 +openssl genpkey -out ${CLIENT}.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048 #generate the Certificate Signing Request for client -openssl req -new -key ${CLIENT}.key -out ${CLIENT}.csr -subj "/CN=KusciaAPIClient" >/dev/null 2>&1 +openssl req -new -key ${CLIENT}.key -out ${CLIENT}.csr -subj "/CN=KusciaAPIClient" #sign it with Root CA for client openssl x509 -req -in ${CLIENT}.csr \ -CA ca.crt -CAkey ca.key \ -days 1000 -sha256 -CAcreateserial \ - -out ${CLIENT}.crt >/dev/null 2>&1 + -out ${CLIENT}.crt popd >/dev/null || exit diff --git a/scripts/deploy/kuscia.sh b/scripts/deploy/kuscia.sh index 658b9f50..8bf57d15 100755 --- a/scripts/deploy/kuscia.sh +++ b/scripts/deploy/kuscia.sh @@ -321,9 +321,9 @@ function init_kuscia_conf_file() { pre_check "${PWD}/${domain_ctr}" if [[ "${domain_type}" = "lite" ]]; then token=$(docker exec -it "${master_ctr}" scripts/deploy/add_domain_lite.sh "${domain_id}" | tr -d '\r\n') - docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode "${domain_type}" --domain "${domain_id}" --master-endpoint ${master_endpoint} --lite-deploy-token ${token} > "${kuscia_conf_file}" + docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode "${domain_type}" --domain "${domain_id}" --master-endpoint ${master_endpoint} --lite-deploy-token ${token} > "${kuscia_conf_file}" 2>&1 || cat "${kuscia_conf_file}" else - docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode "${domain_type}" --domain "${domain_id}" > "${kuscia_conf_file}" + docker run -it --rm ${KUSCIA_IMAGE} kuscia init --mode "${domain_type}" --domain "${domain_id}" > "${kuscia_conf_file}" 2>&1 || cat "${kuscia_conf_file}" fi wrap_kuscia_config_file ${kuscia_conf_file} } @@ -452,14 +452,14 @@ function start_kuscia_container() { echo -e "${GREEN}skip importing sf image${NC}" elif [[ ${IMPORT_SF_IMAGE} = "secretflow" ]]; then if [[ "$domain_type" != "master" ]] && [[ ${runtime} == "runc" ]]; then - docker run --rm $KUSCIA_IMAGE cat ${CTR_ROOT}/scripts/deploy/import_engine_image.sh > import_engine_image.sh && chmod u+x import_engine_image.sh - bash import_engine_image.sh ${domain_ctr} ${SECRETFLOW_IMAGE} - rm -rf import_engine_image.sh + docker run --rm $KUSCIA_IMAGE cat ${CTR_ROOT}/scripts/deploy/register_app_image.sh > register_app_image.sh && chmod u+x register_app_image.sh + bash register_app_image.sh -c ${domain_ctr} -i ${SECRETFLOW_IMAGE} --import + rm -rf register_app_image.sh fi fi if [[ "$domain_type" != "lite" ]]; then - docker exec -it "${domain_ctr}" scripts/deploy/create_secretflow_app_image.sh "${SECRETFLOW_IMAGE}" + docker exec -it "${domain_ctr}" scripts/deploy/register_app_image.sh -i "${SECRETFLOW_IMAGE}" -m log "Create secretflow app image done" fi log "$domain_type domain '${domain_id}' deployed successfully" @@ -616,8 +616,8 @@ function start_cxp_cluster() { docker exec -it ${alice_master_ctr} scripts/deploy/join_to_host.sh ${alice_domain} ${bob_domain} https://${bob_ctr}:1080 -i false -x ${alice_master_domain} -p ${p2p_protocol} docker exec -it ${alice_master_ctr} scripts/deploy/join_to_host.sh $alice_master_domain ${alice_domain} http://${alice_ctr}:1080 -i false -p ${p2p_protocol} docker exec -it ${bob_ctr} scripts/deploy/join_to_host.sh ${bob_domain} ${alice_domain} http://${alice_ctr}:1080 -i false -x ${alice_master_domain} -p ${p2p_protocol} - docker exec -it ${bob_ctr} scripts/deploy/join_to_host.sh ${alice_domain} ${bob_domain} https://${bob_ctr}:1080 -i false -x ${alice_master_domain} -p ${p2p_protocol} - docker exec -it ${alice_master_ctr} scripts/deploy/join_to_host.sh ${bob_domain} ${alice_domain} http://${alice_ctr}:1080 -i false -x ${alice_master_domain} -p ${p2p_protocol} + docker exec -it ${bob_ctr} scripts/deploy/join_to_host.sh ${alice_domain} ${bob_domain} https://${bob_ctr}:1080 -i false -p ${p2p_protocol} + docker exec -it ${alice_master_ctr} scripts/deploy/join_to_host.sh ${bob_domain} ${alice_domain} http://${alice_ctr}:1080 -i false -p ${p2p_protocol} fi docker exec -it ${alice_ctr} scripts/deploy/init_example_data.sh ${alice_domain} diff --git a/scripts/deploy/register_app_image.sh b/scripts/deploy/register_app_image.sh new file mode 100755 index 00000000..27d2e76d --- /dev/null +++ b/scripts/deploy/register_app_image.sh @@ -0,0 +1,159 @@ +#!/bin/bash +# +# Copyright 2023 Ant Group Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +GREEN='\033[0;32m' +NC='\033[0m' +RED='\033[31m' +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P) +usage="$(basename "$0") [OPTIONS] + +OPTIONS: + -h [optional] show this help text + -c [mandatory] kuscia container name + -i [mandatory] app docker image info + -f [optional] kuscia appImage template file full path. the recommended template file naming rule is {appImage name}.yaml under the same directory as tool script. otherwise the file full path must be specified +example: + ./register_app_image.sh -c root-kuscia-autonomy-alice -f ./secretflow-image.yaml -i secretflow-registry.cn-hangzhou.cr.aliyuncs.com/secretflow/secretflow-lite-anolis8:latest --import +" +NEW_ARGS=() +IMPORT=false +for arg in "$@"; do + case $arg in + --import) + IMPORT=true + ;; + *) + NEW_ARGS+=("$arg") + ;; + esac +done +set -- "${NEW_ARGS[@]}" +while getopts 'hc:i:mf:' option; do + case "$option" in + h) + echo "$usage" + exit 0 + ;; + c) + KUSCIA_CONTAINER_NAME=$OPTARG + ;; + i) + IMAGE=$OPTARG + ;; + m) + DEPLOY=ture + ;; + f) + APP_IMAGE_FILE=$OPTARG + ;; + :) + printf "missing argument for -%s\n" "$OPTARG" >&2 + exit 1 + ;; + \?) + printf "illegal option: -%s\n" "$OPTARG" >&2 + exit 1 + ;; + esac +done + +function import_engine_image() { + if docker exec -it ${KUSCIA_CONTAINER_NAME} crictl inspecti ${IMAGE} >/dev/null 2>&1; then + echo -e "${GREEN}Image '${IMAGE}' already exists in container ${KUSCIA_CONTAINER_NAME}${NC}" + else + if docker image inspect ${IMAGE} >/dev/null 2>&1; then + echo -e "${GREEN}Found the engine image '${IMAGE}' on host${NC}" + else + echo -e "${GREEN}Not found the engine image '${IMAGE}' on host${NC}" + echo -e "${GREEN}Start pulling image '${IMAGE}' ...${NC}" + docker pull ${IMAGE} + fi + local image_tag=$(echo ${IMAGE} | cut -d ':' -f 2) + echo -e "${GREEN}Start importing image '${IMAGE}' Please be patient...${NC}" + + local image_tar=/tmp/${image_tag}.tar + docker save ${IMAGE} -o ${image_tar} + docker exec -it ${KUSCIA_CONTAINER_NAME} ctr -a=/home/kuscia/containerd/run/containerd.sock -n=k8s.io images import ${image_tar} + if docker exec -it ${KUSCIA_CONTAINER_NAME} crictl inspecti ${IMAGE} >/dev/null 2>&1; then + echo -e "${GREEN}image ${IMAGE} import successfully${NC}" + else + echo -e "${RED}error: ${IMAGE} import failed${NC}" + fi + rm -rf ${image_tar} + fi +} + +function apply_appimage_crd() { + local image_repo + local image_tag + if [[ "${IMAGE}" == *":"* ]]; then + image_repo=${IMAGE%%:*} + image_tag=${IMAGE##*:} + fi + if [[ ! -f $APP_IMAGE_FILE ]]; then + echo -e "${RED}${APP_IMAGE_FILE} does not exist, register fail${NC}" + else + image_line=$(awk '/^ image:/{print NR; exit}' $APP_IMAGE_FILE) + head -n "$((image_line - 1))" $APP_IMAGE_FILE > /tmp/engine_appimage.yaml + echo -e " image:\n name: ${image_repo}\n tag: ${image_tag}" >> /tmp/engine_appimage.yaml + docker exec -it ${KUSCIA_CONTAINER_NAME} kubectl apply -f /tmp/engine_appimage.yaml + rm -rf /tmp/engine_appimage.yaml + fi +} + +function register_default_app_image() { + local image_repo + local image_tag + if [[ "${IMAGE}" == *":"* ]]; then + image_repo=${IMAGE%%:*} + image_tag=${IMAGE##*:} + fi + local app_type=$(echo "${image_repo}" | awk -F'/' '{print $NF}' | awk -F'-' '{print $1}') + if [[ ${app_type} != "psi" ]]; then + app_type="secretflow" + fi + app_image_template=$(sed "s!{{.SF_IMAGE_NAME}}!'${image_repo}'!g; + s!{{.SF_IMAGE_TAG}}!'${image_tag}'!g" \ + < "${ROOT}/scripts/templates/app_image.${app_type}.yaml") + echo "${app_image_template}" | kubectl apply -f - +} + +function register_app_image() { + if [[ -z "${KUSCIA_CONTAINER_NAME}" ]] || [[ -z "${IMAGE}" ]]; then + echo -e "${RED}KUSCIA_CONTAINER_NAME and IMAGE must not be empty.${NC}" + echo -e "${RED}$usage${NC}" + else + if ! docker ps -a --format '{{.Names}}' | grep -q "^${KUSCIA_CONTAINER_NAME}$"; then + echo -e "${RED}Container ${KUSCIA_CONTAINER_NAME} does not exist.${NC}" + else + if [[ "${IMPORT}" == "true" ]]; then + import_engine_image + fi + if [[ "${APP_IMAGE_FILE}" != "" ]]; then + apply_appimage_crd + fi + fi + fi +} + +if [[ "${DEPLOY}" = "ture" ]]; then + register_default_app_image +else + register_app_image +fi \ No newline at end of file diff --git a/scripts/deploy/start_standalone.sh b/scripts/deploy/start_standalone.sh index 4dd85459..496abfee 100755 --- a/scripts/deploy/start_standalone.sh +++ b/scripts/deploy/start_standalone.sh @@ -315,7 +315,7 @@ function start_lite() { # init kuscia.yaml pre_check ${data_path} csr_token=$(docker exec -it "${master_ctr}" scripts/deploy/add_domain_lite.sh "${domain_id}" "${master_domain}" | tr -d '\r\n') - docker run -it --rm ${IMAGE} kuscia init --mode Lite --domain "${domain_id}" --master-endpoint "${master_endpoint}" --lite-deploy-token "${csr_token}" >"${kuscia_config_file}" + docker run -it --rm ${IMAGE} kuscia init --mode Lite --domain "${domain_id}" --master-endpoint "${master_endpoint}" --lite-deploy-token "${csr_token}" >"${kuscia_config_file}" 2>&1 || cat "${kuscia_config_file}" wrap_kuscia_config_file "${kuscia_config_file}" createVolume "${domain_ctr}-containerd" @@ -353,7 +353,7 @@ function start_master() { # init kuscia.yaml mkdir -p "${conf_dir}" kuscia_config_file="${conf_dir}/kuscia.yaml" - docker run -it --rm ${IMAGE} kuscia init --mode Master --domain "${master_domain}" >"${kuscia_config_file}" + docker run -it --rm ${IMAGE} kuscia init --mode Master --domain "${master_domain}" >"${kuscia_config_file}" 2>&1 || cat "${kuscia_config_file}" # start kuscia docker run -dit --name=${master_ctr} --hostname=${master_ctr} --restart=always --network=${NETWORK_NAME} -m $MASTER_MEMORY_LIMIT ${env_flag} \ @@ -614,7 +614,7 @@ function start_autonomy() { # init kuscia.yaml pre_check ${data_path} - docker run -it --rm ${IMAGE} kuscia init --mode Autonomy --domain "${domain_id}" >"${kuscia_config_file}" + docker run -it --rm ${IMAGE} kuscia init --mode Autonomy --domain "${domain_id}" >"${kuscia_config_file}" 2>&1 || cat "${kuscia_config_file}" wrap_kuscia_config_file "${kuscia_config_file}" "${p2p_protocol}" createVolume "${domain_ctr}-containerd" diff --git a/scripts/templates/app_image.secretflow.yaml b/scripts/templates/app_image.secretflow.yaml index 71912315..cc8482cc 100644 --- a/scripts/templates/app_image.secretflow.yaml +++ b/scripts/templates/app_image.secretflow.yaml @@ -22,7 +22,7 @@ spec: - -c - "python -m secretflow.kuscia.entry ./kuscia/task-config.conf" configVolumeMounts: - - mountPath: /root/kuscia/task-config.conf + - mountPath: ./kuscia/task-config.conf subPath: task-config.conf name: secretflow ports: diff --git a/test/util/utils.go b/test/util/utils.go index b8419bf6..ae765b1f 100644 --- a/test/util/utils.go +++ b/test/util/utils.go @@ -23,6 +23,7 @@ import ( "encoding/pem" "fmt" "math/big" + "strings" "testing" "time" @@ -32,8 +33,9 @@ import ( st "k8s.io/kubernetes/pkg/scheduler/testing" "github.com/google/uuid" - kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" "github.com/stretchr/testify/assert" + + kusciaapisv1alpha1 "github.com/secretflow/kuscia/pkg/crd/apis/kuscia/v1alpha1" ) func MakeTaskResource(namespace, name string, min int, creationTime *time.Time) *kusciaapisv1alpha1.TaskResource { @@ -173,7 +175,14 @@ func NewFakeSharedLister(pods []*corev1.Pod, nodes []*corev1.Node) framework.Sha } func MakeBase64EncodeCert(t *testing.T) string { + return base64.StdEncoding.EncodeToString(makeCertificate(t)) +} +func MakeCertString(t *testing.T) string { + return strings.TrimSuffix(string(makeCertificate(t)), "\n") +} + +func makeCertificate(t *testing.T) []byte { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) assert.NoError(t, err) @@ -190,7 +199,6 @@ func MakeBase64EncodeCert(t *testing.T) string { derBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &privateKey.PublicKey, privateKey) assert.NoError(t, err) - certBuf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - return base64.StdEncoding.EncodeToString(certBuf) + return certBuf } diff --git a/thirdparty/fate/cmd/job/job.go b/thirdparty/fate/cmd/job/job.go index 97a0ee60..f6065d23 100644 --- a/thirdparty/fate/cmd/job/job.go +++ b/thirdparty/fate/cmd/job/job.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package fatejob import ( diff --git a/thirdparty/fate/cmd/main.go b/thirdparty/fate/cmd/main.go index 8543c65a..668fe5d4 100644 --- a/thirdparty/fate/cmd/main.go +++ b/thirdparty/fate/cmd/main.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package main import ( diff --git a/thirdparty/fate/cmd/modlules/modules.go b/thirdparty/fate/cmd/modlules/modules.go index c8ebd9ea..99d14ea1 100644 --- a/thirdparty/fate/cmd/modlules/modules.go +++ b/thirdparty/fate/cmd/modlules/modules.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//nolint:dupl +//nolint:dulp package modules import ( diff --git a/thirdparty/fate/scripts/templates/fate/hybrid_job.yaml b/thirdparty/fate/scripts/templates/fate/hybrid_job.yaml index a4115d06..4a0d8fba 100644 --- a/thirdparty/fate/scripts/templates/fate/hybrid_job.yaml +++ b/thirdparty/fate/scripts/templates/fate/hybrid_job.yaml @@ -2,6 +2,7 @@ apiVersion: kuscia.secretflow/v1alpha1 kind: KusciaJob metadata: name: hybrid-job + namespace: cross-domain spec: initiator: alice scheduleMode: Strict