├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── go.yml │ ├── image.yaml │ └── license.yaml ├── .gitignore ├── .license ├── README.md └── dependency_decisions.yml ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── api ├── addtoscheme_morphling_v1alpha1.go ├── apis.go └── v1alpha1 │ ├── groupversion_info.go │ ├── grpc_proto │ ├── grpc_algorithm │ │ ├── api.proto │ │ ├── go │ │ │ └── api.pb.go │ │ ├── proto.sh │ │ └── python3 │ │ │ ├── api_pb2.py │ │ │ └── api_pb2_grpc.py │ ├── grpc_predict │ │ ├── predict.proto │ │ ├── proto.sh │ │ └── python3 │ │ │ ├── predict_pb2.py │ │ │ └── predict_pb2_grpc.py │ ├── grpc_storage │ │ ├── api.proto │ │ ├── go │ │ │ └── api.pb.go │ │ ├── proto.sh │ │ └── python3 │ │ │ ├── api_pb2.py │ │ │ └── api_pb2_grpc.py │ ├── grpc_storage_v2 │ │ ├── api.proto │ │ ├── proto.sh │ │ └── python3 │ │ │ ├── api_pb2.py │ │ │ └── api_pb2_grpc.py │ └── health │ │ ├── build.sh │ │ ├── health.pb.go │ │ ├── health.proto │ │ ├── health.swagger.json │ │ └── python │ │ ├── health_pb2.py │ │ └── health_pb2_grpc.py │ ├── llmserviceversion_types.go │ ├── profilingexperiment_types.go │ ├── trial_types.go │ └── zz_generated.deepcopy.go ├── cmd ├── algorithm │ └── grid │ │ ├── Dockerfile │ │ ├── main.py │ │ ├── python │ │ └── requirements.txt ├── controllers │ ├── Dockerfile │ └── main.go └── db-manager │ ├── Dockerfile │ ├── main.go │ └── main_test.go ├── config ├── certmanager │ ├── certificate.yaml │ └── kustomizeconfig.yaml ├── crd │ ├── bases │ │ ├── morphling.kubedl.io_llmserviceversions.yaml │ │ ├── morphling.kubedl.io_profilingexperiments.yaml │ │ └── morphling.kubedl.io_trials.yaml │ ├── kustomizeconfig.yaml │ └── patches │ │ ├── cainjection_in_profilingexperiments.yaml │ │ ├── cainjection_in_trials.yaml │ │ ├── webhook_in_profilingexperiments.yaml │ │ └── webhook_in_trials.yaml ├── default │ ├── manager_auth_proxy_patch.yaml │ ├── manager_webhook_patch.yaml │ └── webhookcainjection_patch.yaml ├── manager │ └── manager.yaml ├── prometheus │ └── monitor.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── profilingexperiment_editor_role.yaml │ ├── profilingexperiment_viewer_role.yaml │ ├── role.yaml │ ├── role_binding.yaml │ ├── sampling_editor_role.yaml │ ├── sampling_viewer_role.yaml │ ├── service_editor_role.yaml │ ├── service_viewer_role.yaml │ ├── trial_editor_role.yaml │ ├── trial_viewer_role.yaml │ ├── util_editor_role.yaml │ └── util_viewer_role.yaml ├── samples │ ├── morphling_v1alpha1_profilingexperiment.yaml │ ├── morphling_v1alpha1_trial.yaml │ └── morphling_v1alpha1_util.yaml └── webhook │ ├── kustomizeconfig.yaml │ └── service.yaml ├── console ├── Dockerfile ├── README.md ├── backend │ ├── main.go │ └── pkg │ │ ├── client │ │ └── client.go │ │ ├── configmap │ │ └── configmap.yaml │ │ ├── constant │ │ └── constant.go │ │ ├── handlers │ │ ├── data.go │ │ ├── experiment.go │ │ └── llm_service_version.go │ │ ├── routers │ │ ├── api │ │ │ ├── common.go │ │ │ ├── data.go │ │ │ ├── experiment.go │ │ │ └── llm_service_version.go │ │ └── router.go │ │ └── utils │ │ ├── common.go │ │ ├── data_structures.go │ │ └── redirects.go └── frontend │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc.js │ ├── .stylelintrc.js │ ├── README.md │ ├── config │ ├── config.dev.js │ ├── config.js │ ├── defaultSettings.js │ ├── proxy.js │ └── routes.js │ ├── jest.config.js │ ├── jsconfig.json │ ├── mock │ ├── listTableList.js │ ├── llmServiceVersion.js │ ├── morphling.js │ ├── notices.js │ ├── route.js │ └── user.js │ ├── package.json │ ├── public │ ├── CNAME │ ├── MORPHLING_word.svg │ ├── favicon.ico │ ├── footer.svg │ ├── home_bg.png │ ├── icons │ │ ├── icon-128x128.png │ │ ├── icon-192x192.png │ │ └── icon-512x512.png │ ├── pro_icon.png │ └── pro_icon.svg │ ├── src │ ├── assets │ │ ├── logo.png │ │ └── logo.svg │ ├── components │ │ ├── Authorized │ │ │ ├── Authorized.jsx │ │ │ ├── AuthorizedRoute.jsx │ │ │ ├── CheckPermissions.jsx │ │ │ ├── PromiseRender.jsx │ │ │ ├── Secured.jsx │ │ │ ├── index.jsx │ │ │ └── renderAuthorize.js │ │ ├── ExperimentStatus │ │ │ └── index.js │ │ ├── GlobalHeader │ │ │ ├── AvatarDropdown.jsx │ │ │ ├── NoticeIconView.jsx │ │ │ ├── RightContent.jsx │ │ │ └── index.less │ │ ├── HeaderDropdown │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── HeaderSearch │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── NoticeIcon │ │ │ ├── NoticeList.jsx │ │ │ ├── NoticeList.less │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── PageLoading │ │ │ └── index.jsx │ │ └── PodStatus │ │ │ └── index.js │ ├── e2e │ │ ├── __mocks__ │ │ │ └── antd-pro-merge-less.js │ │ └── baseLayout.e2e.js │ ├── global.jsx │ ├── global.less │ ├── layouts │ │ ├── BasicLayout.jsx │ │ ├── BlankLayout.jsx │ │ ├── SecurityLayout.jsx │ │ ├── UserLayout.jsx │ │ └── UserLayout.less │ ├── locales │ │ ├── en-US.js │ │ ├── en-US │ │ │ ├── component.js │ │ │ ├── globalHeader.js │ │ │ ├── menu.js │ │ │ ├── pages.js │ │ │ ├── pwa.js │ │ │ ├── settingDrawer.js │ │ │ └── settings.js │ │ ├── zh-CN.js │ │ └── zh-CN │ │ │ ├── component.js │ │ │ ├── globalHeader.js │ │ │ ├── menu.js │ │ │ ├── pages.js │ │ │ ├── pwa.js │ │ │ ├── settingDrawer.js │ │ │ └── settings.js │ ├── manifest.json │ ├── models │ │ ├── global.js │ │ ├── login.js │ │ ├── setting.js │ │ └── user.js │ ├── pages │ │ ├── 404.jsx │ │ ├── Admin.jsx │ │ ├── ClusterInfo │ │ │ ├── index.jsx │ │ │ ├── service.js │ │ │ └── style.less │ │ ├── ExperimentCreate │ │ │ ├── components │ │ │ │ ├── FooterToolbar │ │ │ │ │ ├── index.jsx │ │ │ │ │ └── index.less │ │ │ │ ├── InitiForm.js │ │ │ │ ├── SubmitModal.tsx │ │ │ │ └── TableForm.jsx │ │ │ ├── index.jsx │ │ │ ├── service.js │ │ │ └── style.less │ │ ├── ExperimentDetail │ │ │ ├── index.jsx │ │ │ ├── service.js │ │ │ └── style.less │ │ ├── Experiments │ │ │ ├── index.jsx │ │ │ └── service.js │ │ ├── LLMServiceVersion │ │ │ ├── index.jsx │ │ │ ├── service.js │ │ │ └── style.less │ │ ├── User │ │ │ └── login │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ ├── Welcome.jsx │ │ ├── Welcome.less │ │ └── document.ejs │ ├── service-worker.js │ ├── services │ │ ├── login.js │ │ └── user.js │ └── utils │ │ ├── Authorized.js │ │ ├── authority.js │ │ ├── iconfont.js │ │ ├── request.js │ │ ├── utils.js │ │ ├── utils.less │ │ └── utils.test.js │ └── tests │ ├── PuppeteerEnvironment.js │ ├── beforeTest.js │ ├── getBrowser.js │ └── run-tests.js ├── docs ├── debug_guide.md ├── img │ ├── imple.png │ ├── logo.png │ ├── logo_center.png │ ├── stack.png │ ├── ui.png │ └── workflow.png ├── quick_start.md ├── startup_flags.md └── workflow-design.md ├── examples ├── experiment │ ├── experiment-grid.yaml │ ├── experiment-mnist-grid.yaml │ └── experiment-mobilenet-grid.yaml └── trial │ └── trial-mobilenet.yaml ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt ├── helm └── morphling │ ├── .helmignore │ ├── Chart.yaml │ ├── crds │ ├── tuning.kubedl.io_profilingexperiments.yaml │ └── tuning.kubedl.io_trials.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── algorithm │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── configmap │ │ └── morphling-config.yaml │ ├── controllers │ │ ├── controller.yaml │ │ ├── rbac.yaml │ │ └── service.yaml │ ├── db-manager │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── mysql-db │ │ ├── deployment.yaml │ │ ├── secret.yaml │ │ └── service.yaml │ ├── pv │ │ ├── pv.yaml │ │ └── pvc.yaml │ ├── role.yaml │ ├── tests │ │ └── test-connection.yaml │ └── ui │ │ ├── deployment.yaml │ │ ├── rbac.yaml │ │ └── service.yaml │ └── values.yaml ├── manifests ├── algorithm │ ├── deployment.yaml │ ├── kustomization.yaml │ └── service.yaml ├── configmap │ ├── kustomization.yaml │ └── morphling-config.yaml ├── controllers │ ├── controller.yaml │ ├── kustomization.yaml │ ├── rbac.yaml │ └── service.yaml ├── db-manager │ ├── deployment.yaml │ ├── kustomization.yaml │ └── service.yaml ├── kustomization.yaml ├── mysql-db │ ├── deployment.yaml │ ├── kustomization.yaml │ ├── secret.yaml │ └── service.yaml ├── pv │ ├── kustomization.yaml │ ├── pv.yaml │ └── pvc.yaml └── ui │ ├── deployment.yaml │ ├── kustomization.yaml │ ├── rbac.yaml │ └── service.yaml ├── pkg ├── algorithm │ └── v1alpha1 │ │ ├── grid │ │ ├── __init__.py │ │ ├── base_service.py │ │ ├── mock_client.py │ │ ├── mock_client_test.go │ │ └── service.py │ │ └── internal │ │ ├── __init__.py │ │ └── base_health_service.py ├── client │ ├── Dockerfile │ ├── api_pb2.py │ ├── api_pb2_grpc.py │ ├── image.jpg │ ├── morphling_client.py │ ├── morphling_client_mnist.py │ ├── morphling_client_test.py │ └── requirements.txt ├── client_locust │ ├── Dockerfile │ ├── README.md │ ├── api_pb2.py │ ├── api_pb2_grpc.py │ ├── image.jpg │ ├── invokust │ │ ├── __init__.py │ │ ├── loadtest.py │ │ ├── prometheus_exporter.py │ │ └── settings.py │ ├── jobtemplate.yaml │ ├── locust_grpc.py │ ├── locustfile_grpcuser.py │ ├── locustfile_httpuser.py │ ├── morphling_client_locust.py │ └── requirements.txt ├── controllers │ ├── add_experiment.go │ ├── add_trial.go │ ├── consts │ │ └── const.go │ ├── controller.go │ ├── experiment │ │ ├── profilingexperiment_controller.go │ │ ├── profilingexperiment_controller_test.go │ │ ├── sampling_client │ │ │ └── sampling_client.go │ │ ├── status_util.go │ │ └── trials.go │ ├── trial │ │ ├── client_job.go │ │ ├── dbclient │ │ │ └── dbclient.go │ │ ├── server_deploy.go │ │ ├── status_util.go │ │ ├── trial_controller.go │ │ └── trial_controller_test.go │ └── util │ │ ├── trial.go │ │ └── util.go ├── grpc_client │ ├── .dockerignore │ ├── Dockerfile │ ├── api_pb2.py │ ├── api_pb2_grpc.py │ ├── morphling_client.py │ ├── predict_pb2.py │ ├── predict_pb2_grpc.py │ └── requirements.txt ├── mock │ ├── db │ │ └── db.go │ ├── profilingexperiment │ │ └── sampling │ │ │ └── sampling.go │ └── trial │ │ └── mockdbclient.go ├── models │ └── 1628583487 │ │ ├── assets │ │ └── saved_model.json │ │ ├── saved_model.pb │ │ └── variables │ │ ├── checkpoint │ │ ├── variables.data-00000-of-00001 │ │ └── variables.index ├── server │ ├── .dockerignore │ ├── Dockerfile │ ├── main.py │ ├── predict_pb2.py │ ├── predict_pb2_grpc.py │ └── requirements.txt ├── storage │ └── backends │ │ ├── config.go │ │ ├── interface.go │ │ ├── mysql.go │ │ ├── query.go │ │ └── storage_test.go └── test_util │ ├── constant.go │ └── setup.go └── script ├── clean_python_code.sh ├── deploy.sh ├── docker_build.sh ├── docker_pull.sh ├── docker_push.sh ├── helm_chart.sh ├── mockgen.sh └── undeploy.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Tell us about a problem you are experiencing 4 | title: "[BUG]" 5 | labels: kind/bug 6 | assignees: lwangbm 7 | --- 8 | 9 | 10 | **What steps did you take and what happened:** 11 | [A clear and concise description of what the bug is.] 12 | 13 | 14 | **What did you expect to happen:** 15 | 16 | 17 | **Anything else we need to know?**: 18 | 19 | **Environment:** 20 | - Morphling version: 21 | - Minikube version (`minikube version`): 22 | - Kubernetes version (use `kubectl version`): 23 | - OS (e.g: `cat /etc/os-release`): 24 | - Kernel (e.g. `uname -a`): 25 | - Install tools: 26 | - Others: -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature enhancement request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | /kind feature 8 | 9 | **Describe the solution you'd like** 10 | [A clear and concise description of what you want to happen.] 11 | 12 | 13 | **Anything else you would like to add:** 14 | [Miscellaneous information that will assist in solving the issue.] 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ### Ⅰ. What this PR does / why we need it 6 | 7 | 8 | ### II. Does this pull request fix one issue? 9 | 10 | 11 | 12 | ### III. Special notes for reviewers if any. -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | unit-tests: 11 | name: Test 12 | runs-on: ubuntu-latest 13 | env: 14 | GO_VERSION: '1.16' 15 | GOLANGCI_VERSION: 'v1.39' 16 | DOCKER_BUILDX_VERSION: 'v0.4.2' 17 | KUSTOMIZE_VERSION: 'v4.1.3' 18 | KUBEDL_CI: 'true' 19 | KIND_VERSION: 'v0.11.1' 20 | GOPATH: ${{ github.workspace }}/go 21 | defaults: 22 | run: 23 | working-directory: ${{ env.GOPATH }}/src/github.com/alibaba/morphling 24 | 25 | steps: 26 | - name: Check out code 27 | uses: actions/checkout@v2 28 | with: 29 | path: ${{ env.GOPATH }}/src/github.com/alibaba/morphling 30 | 31 | - name: Setup Go 32 | uses: actions/setup-go@v2 33 | with: 34 | go-version: ${{ env.GO_VERSION }} 35 | 36 | - name: Check Go modules 37 | run: | 38 | if [[ ! -z $(go mod tidy && git diff --exit-code) ]]; then 39 | echo "Please run "go mod tidy" to sync Go modules" 40 | exit 1 41 | fi 42 | 43 | - name: Install Kubebuilder 44 | uses: RyanSiu1995/kubebuilder-action@v1.2.1 45 | 46 | - name: Cache Go Dependencies 47 | uses: actions/cache@v2 48 | with: 49 | path: ~/go/pkg/mod 50 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 51 | restore-keys: ${{ runner.os }}-go- 52 | 53 | - name: Run Go test 54 | run: | 55 | go mod download 56 | make test 57 | -------------------------------------------------------------------------------- /.github/workflows/image.yaml: -------------------------------------------------------------------------------- 1 | name: Generate Image Daily 2 | on: 3 | push: 4 | branches: [ main ] 5 | schedule: 6 | # * is a special character in YAML so you have to quote this string 7 | - cron: '30 22 * * *' 8 | jobs: 9 | daily-image: 10 | runs-on: ubuntu-latest 11 | env: 12 | GO_VERSION: '1.16' 13 | GOLANGCI_VERSION: 'v1.39' 14 | DOCKER_BUILDX_VERSION: 'v0.4.2' 15 | KUSTOMIZE_VERSION: 'v4.1.3' 16 | KUBEDL_CI: 'true' 17 | KIND_VERSION: 'v0.11.1' 18 | GOPATH: ${{ github.workspace }}/go 19 | defaults: 20 | run: 21 | working-directory: ${{ env.GOPATH }}/src/github.com/alibaba/morphling 22 | steps: 23 | - name: Check out code 24 | uses: actions/checkout@v2 25 | with: 26 | path: ${{ env.GOPATH }}/src/github.com/alibaba/morphling 27 | 28 | - name: Setup Go 29 | uses: actions/setup-go@v2 30 | with: 31 | go-version: ${{ env.GO_VERSION }} 32 | 33 | - name: Check Go modules 34 | run: | 35 | if [[ ! -z $(go mod tidy && git diff --exit-code) ]]; then 36 | echo "Please run "go mod tidy" to sync Go modules" 37 | exit 1 38 | fi 39 | 40 | - name: Install Kubebuilder 41 | uses: RyanSiu1995/kubebuilder-action@v1.2.1 42 | 43 | - name: Cache Go Dependencies 44 | uses: actions/cache@v2 45 | with: 46 | path: ~/go/pkg/mod 47 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 48 | restore-keys: ${{ runner.os }}-go- 49 | 50 | - name: Login to DockerHub 51 | uses: docker/login-action@v1 52 | with: 53 | username: ${{ secrets.DOCKER_USERNAME }} 54 | password: ${{ secrets.DOCKER_PASSWORD }} 55 | 56 | - name: Build image 57 | run: | 58 | make docker-build 59 | make docker-push 60 | -------------------------------------------------------------------------------- /.github/workflows/license.yaml: -------------------------------------------------------------------------------- 1 | name: License 2 | on: 3 | push: 4 | branches: 5 | - main 6 | workflow_dispatch: {} 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | license_check: 13 | runs-on: ubuntu-latest 14 | name: Check for unapproved licenses 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Ruby 18 | uses: ruby/setup-ruby@v1 19 | with: 20 | ruby-version: 2.6 21 | - name: Install dependencies 22 | run: gem install license_finder 23 | - name: Run tests 24 | run: license_finder --decisions_file .license/dependency_decisions.yml -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # python ignore files 2 | __pycache__/ 3 | *.egg-info 4 | 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | bin 12 | 13 | # Test binary, build with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # editor and IDE paraphernalia 20 | .idea 21 | *.swp 22 | *.swo 23 | *~ 24 | .DS_Store 25 | .vscode 26 | 27 | # vendor dir 28 | vendor 29 | 30 | # arch 31 | arch 32 | 33 | # model cache dir 34 | .kubedl_model_cache 35 | 36 | # vllm install package 37 | vllm*.whl 38 | 39 | # backend server executable file 40 | backend-server 41 | -------------------------------------------------------------------------------- /.license/README.md: -------------------------------------------------------------------------------- 1 | # License Checker 2 | 3 | Our license checker CI rely on [LicenseFinder](https://github.com/pivotal/LicenseFinder). 4 | 5 | ## How to add a new license 6 | 7 | LicenseFinder is a ruby project, so make sure you have ruby installed. 8 | 9 | ### Install the tool 10 | 11 | ```shell 12 | gem install license_finder 13 | ``` 14 | 15 | ### Add a license 16 | 17 | ```shell 18 | license_finder permitted_licenses add MIT --decisions_file .license/dependency_decisions.yml 19 | ``` 20 | 21 | ### Current licenses 22 | #### Apache 2.0 23 | #### MIT 24 | #### New BSD 25 | #### Simplified BSD 26 | #### Mozilla Public License 2.0 27 | #### ISC -------------------------------------------------------------------------------- /.license/dependency_decisions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - - :permit 3 | - Apache 2.0 4 | - :who: 5 | :why: 6 | :versions: [] 7 | :when: 2021-08-03 14:33:03.324221000 Z 8 | - - :permit 9 | - MIT 10 | - :who: 11 | :why: 12 | :versions: [] 13 | :when: 2021-08-03 14:33:03.324221000 Z 14 | - - :permit 15 | - New BSD 16 | - :who: 17 | :why: 18 | :versions: [] 19 | :when: 2021-08-03 14:34:51.293777000 Z 20 | - - :permit 21 | - Simplified BSD 22 | - :who: 23 | :why: 24 | :versions: [] 25 | :when: 2021-08-03 14:35:24.884647000 Z 26 | - - :permit 27 | - Mozilla Public License 2.0 28 | - :who: 29 | :why: 30 | :versions: [] 31 | :when: 2021-08-03 14:35:43.730405000 Z 32 | - - :permit 33 | - ISC 34 | - :who: 35 | :why: 36 | :versions: [] 37 | :when: 2021-08-03 14:36:03.346954000 Z 38 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | version: "2" 2 | domain: kubedl.io 3 | repo: github.com/alibaba/morphling 4 | -------------------------------------------------------------------------------- /api/addtoscheme_morphling_v1alpha1.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Alibaba Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package api 18 | 19 | import ( 20 | "github.com/alibaba/morphling/api/v1alpha1" 21 | ) 22 | 23 | func init() { 24 | AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) 25 | } 26 | -------------------------------------------------------------------------------- /api/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Alibaba Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package api 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/runtime" 21 | ) 22 | 23 | var AddToSchemes runtime.SchemeBuilder 24 | 25 | func AddToScheme(s *runtime.Scheme) error { 26 | return AddToSchemes.AddToScheme(s) 27 | } 28 | -------------------------------------------------------------------------------- /api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Alibaba Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains API Schema definitions for the morphling v1alpha1 API group 18 | // +kubebuilder:object:generate=true 19 | // +groupName=morphling.kubedl.io 20 | // +kubebuilder:subresource:status 21 | package v1alpha1 22 | 23 | import ( 24 | "k8s.io/apimachinery/pkg/runtime/schema" 25 | "sigs.k8s.io/controller-runtime/pkg/scheme" 26 | ) 27 | 28 | const ( 29 | Group = "morphling.kubedl.io" 30 | Version = "v1alpha1" 31 | ) 32 | 33 | var ( 34 | // SchemeGroupVersion is group version used to register these objects 35 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 36 | 37 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 38 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 39 | 40 | // AddToScheme adds the types in this group-version to the given scheme. 41 | AddToScheme = SchemeBuilder.AddToScheme 42 | ) 43 | 44 | // Resource takes an unqualified resource and returns a Group-qualified GroupResource. 45 | func Resource(resource string) schema.GroupResource { 46 | return SchemeGroupVersion.WithResource(resource).GroupResource() 47 | } 48 | -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_algorithm/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package api.suggestion; 3 | option go_package = "../grpc_algorithm/go"; 4 | //import "google/api/annotations.proto"; 5 | 6 | service Suggestion { 7 | rpc GetSuggestions(SamplingRequest) returns (SamplingResponse); 8 | rpc ValidateAlgorithmSettings(SamplingValidationRequest) returns (SamplingValidationResponse); 9 | } 10 | 11 | message KeyValue { 12 | string key = 1; 13 | string value = 2; 14 | } 15 | 16 | //message AlgorithmExtraSetting { 17 | // repeated KeyValue settings = 1; 18 | //} 19 | 20 | message ParameterAssignments { 21 | repeated KeyValue key_values = 1; 22 | } 23 | 24 | message TrialResult { 25 | repeated KeyValue parameter_assignments = 1; 26 | float object_value = 2; // 27 | } 28 | 29 | //message ExistingResults { 30 | // repeated TrialResult trial_results = 1; 31 | //} 32 | 33 | //message FeasibleSpace { 34 | // repeated string list = 1; 35 | //} 36 | 37 | enum ParameterType { 38 | UNKNOWN_TYPE = 0; 39 | DOUBLE = 1; 40 | INT = 2; 41 | DISCRETE = 3; 42 | CATEGORICAL = 4; 43 | } 44 | 45 | message ParameterSpec { 46 | string name = 1; 47 | ParameterType parameter_type = 2; 48 | repeated string feasible_space = 3; 49 | } 50 | 51 | message SamplingRequest { 52 | bool is_first_request = 1; 53 | string algorithm_name = 2; 54 | repeated KeyValue algorithm_extra_settings = 3; 55 | int32 sampling_number_specified = 4; 56 | int32 required_sampling = 6; 57 | bool is_maximize = 7; 58 | repeated TrialResult existing_results = 8; 59 | repeated ParameterSpec parameters = 9; 60 | } 61 | 62 | message SamplingResponse { 63 | repeated ParameterAssignments assignments_set = 1; 64 | } 65 | 66 | message SamplingValidationRequest { 67 | string algorithm_name = 1; 68 | repeated KeyValue algorithm_extra_settings = 2; 69 | int32 sampling_number_specified = 3; 70 | bool is_maximize = 4; 71 | repeated ParameterSpec parameters = 5; 72 | } 73 | 74 | message SamplingValidationResponse { 75 | } 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_algorithm/proto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PROTO_FILE=api.proto 3 | protoc --go_out=plugins=grpc:./ "$PROTO_FILE" 4 | python3 -m grpc_tools.protoc -I. --python_out=python3 --grpc_python_out=python3 "$PROTO_FILE" -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_predict/predict.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package api.predict; 4 | 5 | option go_package = "../grpc_predict/go"; 6 | 7 | // Define prediction service 8 | service Predictor { 9 | // Perform model inference 10 | rpc Predict(PredictRequest) returns (PredictResponse); 11 | } 12 | 13 | // Prediction request 14 | message PredictRequest { 15 | bytes input_data = 1; // Input data, can be serialized tensor or other formats 16 | map metadata = 2; // Additional metadata 17 | } 18 | 19 | // Prediction response 20 | message PredictResponse { 21 | bytes output_data = 1; // Output data, can be serialized tensor or other formats 22 | map metadata = 2; // Additional metadata 23 | } -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_predict/proto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set the proto file name 4 | export PROTO_FILE=predict.proto 5 | 6 | # Generate Go code 7 | protoc --go_out=. "$PROTO_FILE" 8 | 9 | # Generate Python code 10 | python3 -m grpc_tools.protoc -I. --python_out=python3 --grpc_python_out=python3 "$PROTO_FILE" 11 | 12 | # Output completion information 13 | echo "gRPC code generation completed for $PROTO_FILE" -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_storage/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package api.storage; 3 | option go_package = "../grpc_storage/go"; 4 | 5 | service DB { 6 | rpc SaveResult(SaveResultRequest) returns (SaveResultReply); 7 | rpc GetResult(GetResultRequest) returns (GetResultReply); 8 | } 9 | 10 | message KeyValue { 11 | string key = 1; 12 | string value = 2; 13 | } 14 | 15 | message SaveResultReply { 16 | } 17 | 18 | message SaveResultRequest { 19 | string namespace = 1; 20 | string trial_name = 2; 21 | // string experiment_name = 3; 22 | repeated KeyValue results = 4; 23 | } 24 | 25 | message GetResultRequest { 26 | string namespace = 1; 27 | string trial_name = 2; 28 | // string experiment_name = 3; 29 | } 30 | 31 | message GetResultReply { 32 | string namespace = 1; 33 | string trial_name = 2; 34 | // string experiment_name = 3; 35 | repeated KeyValue results = 4; 36 | } 37 | -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_storage/proto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PROTO_FILE=api.proto 3 | protoc --go_out=plugins=grpc:./ "$PROTO_FILE" 4 | python3 -m grpc_tools.protoc -I. --python_out=python3 --grpc_python_out=python3 "$PROTO_FILE" -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_storage_v2/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package api.storage; 3 | option go_package = "../grpc_storage/go"; 4 | 5 | service DB { 6 | rpc SaveResult(SaveResultRequest) returns (SaveResultReply); 7 | rpc GetResult(GetResultRequest) returns (GetResultReply); 8 | } 9 | 10 | message KeyValue { 11 | string key = 1; 12 | string value = 2; 13 | } 14 | 15 | message SaveResultReply { 16 | } 17 | 18 | message SaveResultRequest { 19 | string namespace = 1; 20 | string trial_name = 2; 21 | // string experiment_name = 3; 22 | repeated KeyValue results = 4; 23 | } 24 | 25 | message GetResultRequest { 26 | string namespace = 1; 27 | string trial_name = 2; 28 | // string experiment_name = 3; 29 | } 30 | 31 | message GetResultReply { 32 | string namespace = 1; 33 | string trial_name = 2; 34 | // string experiment_name = 3; 35 | repeated KeyValue results = 4; 36 | } 37 | -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/grpc_storage_v2/proto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PROTO_FILE=api.proto 3 | protoc --go_out=plugins=grpc:./ "$PROTO_FILE" 4 | python3 -m grpc_tools.protoc -I. --python_out=python3 --grpc_python_out=python3 "$PROTO_FILE" -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/health/build.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | set -e 3 | proto="health.proto" 4 | docker run -it --rm -v $PWD:$(pwd) -w $(pwd) znly/protoc --python_out=plugins=grpc:./python --go_out=plugins=grpc:. -I. $proto 5 | docker run -it --rm -v $PWD:$(pwd) -w $(pwd) znly/protoc --plugin=protoc-gen-grpc=/usr/bin/grpc_python_plugin --python_out=./python --grpc_out=./python -I. $proto 6 | docker run -it --rm -v $PWD:$(pwd) -w $(pwd) znly/protoc --grpc-gateway_out=logtostderr=true:. -I. $proto 7 | docker run -it --rm -v $PWD:$(pwd) -w $(pwd) znly/protoc --swagger_out=logtostderr=true:. -I. $proto 8 | -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/health/health.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package grpc.health.v1; 4 | 5 | service Health { 6 | rpc Check(HealthCheckRequest) returns (HealthCheckResponse); 7 | } 8 | 9 | message HealthCheckRequest { 10 | string service = 1; 11 | } 12 | 13 | message HealthCheckResponse { 14 | enum ServingStatus { 15 | UNKNOWN = 0; 16 | SERVING = 1; 17 | NOT_SERVING = 2; 18 | } 19 | ServingStatus status = 1; 20 | } 21 | -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/health/health.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "health.proto", 5 | "version": "version not set" 6 | }, 7 | "schemes": [ 8 | "http", 9 | "https" 10 | ], 11 | "consumes": [ 12 | "application/json" 13 | ], 14 | "produces": [ 15 | "application/json" 16 | ], 17 | "paths": {}, 18 | "definitions": { 19 | "HealthCheckResponseServingStatus": { 20 | "type": "string", 21 | "enum": [ 22 | "UNKNOWN", 23 | "SERVING", 24 | "NOT_SERVING" 25 | ], 26 | "default": "UNKNOWN" 27 | }, 28 | "v1HealthCheckResponse": { 29 | "type": "object", 30 | "properties": { 31 | "status": { 32 | "$ref": "#/definitions/HealthCheckResponseServingStatus" 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/v1alpha1/grpc_proto/health/python/health_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | import api.v1alpha1.grpc_proto.health.python.health_pb2 as health__pb2 5 | 6 | 7 | class HealthStub(object): 8 | # missing associated documentation comment in .proto file 9 | pass 10 | 11 | def __init__(self, channel): 12 | """Constructor. 13 | 14 | Args: 15 | channel: A grpc_algorithm.Channel. 16 | """ 17 | self.Check = channel.unary_unary( 18 | "/grpc.health.v1.Health/Check", 19 | request_serializer=health__pb2.HealthCheckRequest.SerializeToString, 20 | response_deserializer=health__pb2.HealthCheckResponse.FromString, 21 | ) 22 | 23 | 24 | class HealthServicer(object): 25 | # missing associated documentation comment in .proto file 26 | pass 27 | 28 | def Check(self, request, context): 29 | # missing associated documentation comment in .proto file 30 | pass 31 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 32 | context.set_details("Method not implemented!") 33 | raise NotImplementedError("Method not implemented!") 34 | 35 | 36 | def add_HealthServicer_to_server(servicer, server): 37 | rpc_method_handlers = { 38 | "Check": grpc.unary_unary_rpc_method_handler( 39 | servicer.Check, 40 | request_deserializer=health__pb2.HealthCheckRequest.FromString, 41 | response_serializer=health__pb2.HealthCheckResponse.SerializeToString, 42 | ), 43 | } 44 | generic_handler = grpc.method_handlers_generic_handler( 45 | "grpc.health.v1.Health", rpc_method_handlers 46 | ) 47 | server.add_generic_rpc_handlers((generic_handler,)) 48 | -------------------------------------------------------------------------------- /api/v1alpha1/llmserviceversion_types.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | ) 6 | 7 | // LLMServiceVersionSpec defines the desired state of LLMServiceVersion 8 | type LLMServiceVersionSpec struct { 9 | // Version number of the LLM service 10 | Version string `json:"version"` 11 | 12 | // ModelName is the name of the LLM model 13 | ModelName string `json:"modelName"` 14 | 15 | // CreationTime is the time when this version was created 16 | CreationTime string `json:"creationTime"` 17 | 18 | // AssociatedExperimentSpec is the spec of the associated experiment 19 | AssociatedExperimentSpec ProfilingExperimentSpec `json:"associatedExperimentSpec"` 20 | } 21 | 22 | // LLMServiceVersionStatus defines the observed state of LLMServiceVersion 23 | type LLMServiceVersionStatus struct { 24 | // TestCompletionTime is the time when testing for this version was completed 25 | TestCompletionTime metav1.Time `json:"testCompletionTime,omitempty"` 26 | 27 | // AssociatedExperimentStatus is the status of the associated experiment 28 | AssociatedExperimentStatus ProfilingExperimentStatus `json:"associatedExperimentStatus"` 29 | } 30 | 31 | //+kubebuilder:object:root=true 32 | //+kubebuilder:subresource:status 33 | 34 | // LLMServiceVersion is the Schema for the llmserviceversions API 35 | type LLMServiceVersion struct { 36 | metav1.TypeMeta `json:",inline"` 37 | metav1.ObjectMeta `json:"metadata,omitempty"` 38 | 39 | Spec LLMServiceVersionSpec `json:"spec,omitempty"` 40 | Status LLMServiceVersionStatus `json:"status,omitempty"` 41 | } 42 | 43 | //+kubebuilder:object:root=true 44 | // LLMServiceVersionList contains a list of LLMServiceVersion 45 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 46 | type LLMServiceVersionList struct { 47 | metav1.TypeMeta `json:",inline"` 48 | metav1.ListMeta `json:"metadata,omitempty"` 49 | Items []LLMServiceVersion `json:"items"` 50 | } 51 | 52 | func init() { 53 | SchemeBuilder.Register(&LLMServiceVersion{}, &LLMServiceVersionList{}) 54 | } 55 | -------------------------------------------------------------------------------- /cmd/algorithm/grid/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | ENV TARGET_DIR /morphling 4 | ENV SUGGESTION_DIR cmd/algorithm/grid 5 | ENV PYTHONUNBUFFERED 0 6 | 7 | RUN if [ "$(uname -m)" = "ppc64le" ] || [ "$(uname -m)" = "aarch64" ]; then \ 8 | apt-get -y update && \ 9 | apt-get -y install gfortran libopenblas-dev liblapack-dev && \ 10 | pip install cython 'numpy>=1.13.3'; \ 11 | fi 12 | RUN GRPC_HEALTH_PROBE_VERSION=v0.4.28 && \ 13 | if [ "$(uname -m)" = "ppc64le" ]; then \ 14 | wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-ppc64le; \ 15 | elif [ "$(uname -m)" = "aarch64" ]; then \ 16 | wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-arm64; \ 17 | else \ 18 | wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64; \ 19 | fi && \ 20 | chmod +x /bin/grpc_health_probe 21 | 22 | ADD ./pkg/ ${TARGET_DIR}/pkg/ 23 | ADD ./api/ ${TARGET_DIR}/api/ 24 | ADD ./${SUGGESTION_DIR}/ ${TARGET_DIR}/${SUGGESTION_DIR}/ 25 | WORKDIR ${TARGET_DIR}/${SUGGESTION_DIR} 26 | RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 27 | 28 | RUN chgrp -R 0 ${TARGET_DIR} \ 29 | && chmod -R g+rwX ${TARGET_DIR} 30 | 31 | ENV PYTHONPATH ${TARGET_DIR}:${TARGET_DIR}/api/v1alpha1/grpc_proto/grpc_algorithm/python3:${TARGET_DIR}/api/v1alpha1/grpc_proto/health/python 32 | 33 | ENTRYPOINT ["python", "main.py"] 34 | -------------------------------------------------------------------------------- /cmd/algorithm/grid/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | from concurrent import futures 3 | 4 | import grpc 5 | 6 | from api.v1alpha1.grpc_proto.grpc_algorithm.python3 import api_pb2_grpc 7 | from api.v1alpha1.grpc_proto.health.python import health_pb2_grpc 8 | from pkg.algorithm.v1alpha1.grid.service import BaseService 9 | 10 | _ONE_DAY_IN_SECONDS = 60 * 60 * 24 11 | DEFAULT_PORT = "0.0.0.0:9996" 12 | 13 | 14 | def serve(): 15 | server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) 16 | service = BaseService() 17 | api_pb2_grpc.add_SuggestionServicer_to_server(service, server) 18 | health_pb2_grpc.add_HealthServicer_to_server(service, server) 19 | server.add_insecure_port(DEFAULT_PORT) 20 | print("Listening...") 21 | server.start() 22 | try: 23 | while True: 24 | time.sleep(_ONE_DAY_IN_SECONDS) 25 | except KeyboardInterrupt: 26 | server.stop(0) 27 | 28 | 29 | if __name__ == "__main__": 30 | serve() 31 | -------------------------------------------------------------------------------- /cmd/algorithm/grid/python: -------------------------------------------------------------------------------- 1 | usage: grep [-abcDEFGHhIiJLlmnOoqRSsUVvwxZ] [-A num] [-B num] [-C[num]] 2 | [-e pattern] [-f file] [--binary-files=value] [--color=when] 3 | [--context[=num]] [--directories=action] [--label] [--line-buffered] 4 | [--null] [pattern] [file ...] 5 | -------------------------------------------------------------------------------- /cmd/algorithm/grid/requirements.txt: -------------------------------------------------------------------------------- 1 | grpcio==1.33.2 2 | duecredit===0.7.0 3 | cloudpickle==0.5.6 4 | numpy>=1.13.3 5 | scikit-learn>=0.19.0 6 | scipy>=0.19.1 7 | forestci==0.3 8 | protobuf==3.13.0 9 | googleapis-common-protos==1.6.0 10 | hyperopt==0.2.3 11 | SQLAlchemy==1.3.8 12 | git+https://github.com/AIworx-Labs/chocolate@master 13 | ghalton>=0.6 14 | -------------------------------------------------------------------------------- /cmd/controllers/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:alpine AS build-env 3 | 4 | # Copy in the go src 5 | ADD . /go/src/morphling 6 | 7 | WORKDIR /go/src/morphling/cmd/controllers 8 | # Build 9 | RUN if [ "$(uname -m)" = "ppc64le" ]; then \ 10 | CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build -a -o morphling-controller .; \ 11 | elif [ "$(uname -m)" = "aarch64" ]; then \ 12 | CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -a -o morphling-controller .; \ 13 | else \ 14 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o morphling-controller .; \ 15 | fi 16 | 17 | # Copy the controller-manager into a thin image 18 | FROM alpine:3.7 19 | WORKDIR /app 20 | RUN apk update && apk add ca-certificates 21 | COPY --from=build-env /go/src/morphling/cmd/controllers/morphling-controller . 22 | USER 1000 23 | ENTRYPOINT ["./morphling-controller"] 24 | -------------------------------------------------------------------------------- /cmd/db-manager/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS build-env 2 | # The GOPATH in the image is /go. 3 | ADD . /go/src/morphling 4 | WORKDIR /go/src/morphling/cmd/db-manager 5 | RUN if [ "$(uname -m)" = "ppc64le" ] || [ "$(uname -m)" = "aarch64" ]; then \ 6 | apk --update add git gcc musl-dev && \ 7 | go build -o morphling-storage .; \ 8 | else \ 9 | go build -o morphling-storage .; \ 10 | fi 11 | RUN GRPC_HEALTH_PROBE_VERSION=v0.3.1 && \ 12 | if [ "$(uname -m)" = "ppc64le" ]; then \ 13 | wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-ppc64le; \ 14 | elif [ "$(uname -m)" = "aarch64" ]; then \ 15 | wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-arm64; \ 16 | else \ 17 | wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64; \ 18 | fi && \ 19 | chmod +x /bin/grpc_health_probe 20 | 21 | FROM alpine:3.7 22 | WORKDIR /app 23 | COPY --from=build-env /bin/grpc_health_probe /bin/ 24 | COPY --from=build-env /go/src/morphling/cmd/db-manager/morphling-storage /app/ 25 | ENTRYPOINT ["./morphling-storage"] 26 | CMD ["-w", "kubernetes"] 27 | -------------------------------------------------------------------------------- /config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for 4 | # breaking changes 5 | apiVersion: cert-manager.io/v1alpha2 6 | kind: Issuer 7 | metadata: 8 | name: selfsigned-issuer 9 | namespace: system 10 | spec: 11 | selfSigned: {} 12 | --- 13 | apiVersion: cert-manager.io/v1alpha2 14 | kind: Certificate 15 | metadata: 16 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 17 | namespace: system 18 | spec: 19 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize 20 | dnsNames: 21 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc 22 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local 23 | issuerRef: 24 | kind: Issuer 25 | name: selfsigned-issuer 26 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 27 | -------------------------------------------------------------------------------- /config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | group: apiextensions.k8s.io 8 | path: spec/conversion/webhookClientConfig/service/name 9 | 10 | namespace: 11 | - kind: CustomResourceDefinition 12 | group: apiextensions.k8s.io 13 | path: spec/conversion/webhookClientConfig/service/namespace 14 | create: false 15 | 16 | varReference: 17 | - path: metadata/annotations 18 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_profilingexperiments.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: profilingexperiments.morphling.kubedl.io 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_trials.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: trials.morphling.kubedl.io 9 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_profilingexperiments.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: profilingexperiments.morphling.kubedl.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhookClientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_trials.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: trials.morphling.kubedl.io 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhookClientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | name: https 22 | - name: manager 23 | args: 24 | - "--metrics-addr=127.0.0.1:8080" 25 | - "--enable-leader-election" 26 | -------------------------------------------------------------------------------- /config/default/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: webhook-server-cert 24 | -------------------------------------------------------------------------------- /config/default/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. 3 | apiVersion: admissionregistration.k8s.io/v1beta1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | name: mutating-webhook-configuration 7 | annotations: 8 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 9 | --- 10 | apiVersion: admissionregistration.k8s.io/v1beta1 11 | kind: ValidatingWebhookConfiguration 12 | metadata: 13 | name: validating-webhook-configuration 14 | annotations: 15 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 16 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | control-plane: controller-manager 24 | spec: 25 | containers: 26 | - command: 27 | - /manager 28 | args: 29 | - --enable-leader-election 30 | image: controller:latest 31 | name: manager 32 | resources: 33 | limits: 34 | cpu: 100m 35 | memory: 30Mi 36 | requests: 37 | cpu: 100m 38 | memory: 20Mi 39 | terminationGracePeriodSeconds: 10 40 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | selector: 15 | matchLabels: 16 | control-plane: controller-manager 17 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: ["/metrics"] 7 | verbs: ["get"] 8 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: ["authentication.k8s.io"] 7 | resources: 8 | - tokenreviews 9 | verbs: ["create"] 10 | - apiGroups: ["authorization.k8s.io"] 11 | resources: 12 | - subjectaccessreviews 13 | verbs: ["create"] 14 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | targetPort: https 13 | selector: 14 | control-plane: controller-manager 15 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - configmaps/status 23 | verbs: 24 | - get 25 | - update 26 | - patch 27 | - apiGroups: 28 | - "" 29 | resources: 30 | - events 31 | verbs: 32 | - create 33 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/profilingexperiment_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit profilingexperiments. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: profilingexperiment-editor-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - profilingexperiments 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - morphling.kubedl.io 21 | resources: 22 | - profilingexperiments/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/profilingexperiment_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view profilingexperiments. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: profilingexperiment-viewer-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - profilingexperiments 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - morphling.kubedl.io 17 | resources: 18 | - profilingexperiments/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | kind: ClusterRole 5 | metadata: 6 | creationTimestamp: null 7 | name: manager-role 8 | rules: 9 | - apiGroups: 10 | - apps 11 | resources: 12 | - deployments 13 | verbs: 14 | - create 15 | - delete 16 | - get 17 | - list 18 | - patch 19 | - update 20 | - watch 21 | - apiGroups: 22 | - apps 23 | resources: 24 | - deployments/status 25 | verbs: 26 | - get 27 | - patch 28 | - update 29 | - apiGroups: 30 | - morphling.kubedl.io 31 | resources: 32 | - profilingexperiments 33 | verbs: 34 | - create 35 | - delete 36 | - get 37 | - list 38 | - patch 39 | - update 40 | - watch 41 | - apiGroups: 42 | - morphling.kubedl.io 43 | resources: 44 | - profilingexperiments/status 45 | verbs: 46 | - get 47 | - patch 48 | - update 49 | - apiGroups: 50 | - morphling.kubedl.io 51 | resources: 52 | - trials 53 | verbs: 54 | - create 55 | - delete 56 | - get 57 | - list 58 | - patch 59 | - update 60 | - watch 61 | - apiGroups: 62 | - morphling.kubedl.io 63 | resources: 64 | - trials/status 65 | verbs: 66 | - get 67 | - patch 68 | - update 69 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/sampling_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit samplings. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: sampling-editor-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - samplings 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - morphling.kubedl.io 21 | resources: 22 | - samplings/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/sampling_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view samplings. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: sampling-viewer-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - samplings 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - morphling.kubedl.io 17 | resources: 18 | - samplings/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/service_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit services. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: service-editor-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - services 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - morphling.kubedl.io 21 | resources: 22 | - services/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/service_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view services. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: service-viewer-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - services 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - morphling.kubedl.io 17 | resources: 18 | - services/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/trial_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit trials. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: trial-editor-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - trials 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - morphling.kubedl.io 21 | resources: 22 | - trials/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/trial_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view trials. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: trial-viewer-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - trials 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - morphling.kubedl.io 17 | resources: 18 | - trials/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/util_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit utils. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: util-editor-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - utils 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - morphling.kubedl.io 21 | resources: 22 | - utils/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/util_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view utils. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: util-viewer-role 6 | rules: 7 | - apiGroups: 8 | - morphling.kubedl.io 9 | resources: 10 | - utils 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - morphling.kubedl.io 17 | resources: 18 | - utils/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/samples/morphling_v1alpha1_trial.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: morphling.kubedl.io/v1alpha1 2 | kind: Trial 3 | metadata: 4 | name: trial-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/samples/morphling_v1alpha1_util.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: morphling.kubedl.io/v1alpha1 2 | kind: Util 3 | metadata: 4 | name: util-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | - kind: MutatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | - kind: MutatingWebhookConfiguration 16 | group: admissionregistration.k8s.io 17 | path: webhooks/clientConfig/service/namespace 18 | create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | 24 | varReference: 25 | - path: metadata/annotations 26 | -------------------------------------------------------------------------------- /config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: webhook-service 6 | namespace: system 7 | spec: 8 | ports: 9 | - port: 443 10 | targetPort: 9443 11 | selector: 12 | control-plane: controller-manager 13 | -------------------------------------------------------------------------------- /console/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16.5 as backend-builder 2 | 3 | WORKDIR /workspace 4 | 5 | COPY . . 6 | ENV GOPROXY=https://goproxy.cn,direct 7 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go mod vendor 8 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod=vendor -a -o backend-server console/backend/main.go 9 | 10 | FROM node:12.14.1 as frontend-builder 11 | 12 | WORKDIR /workspace 13 | 14 | COPY console/frontend/ . 15 | RUN rm -rf ./dist && rm -rf ./node_modules && rm -f ./package-lock.json 16 | RUN npm --registry=https://registry.npmmirror.com install --no-optional 17 | RUN npm run build 18 | 19 | FROM alpine:latest 20 | WORKDIR / 21 | 22 | ARG ARG_TZ=Etc/UTC 23 | COPY --from=frontend-builder /workspace/dist ./console/frontend/dist 24 | COPY --from=backend-builder /workspace/backend-server ./backend-server 25 | ENV TZ=$ARG_TZ 26 | RUN apk add -U tzdata 27 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 28 | #RUN chmod +x ./startup.sh 29 | 30 | ENTRYPOINT ["/backend-server"] 31 | -------------------------------------------------------------------------------- /console/README.md: -------------------------------------------------------------------------------- 1 | # Morphling User Interface 2 | 3 | This is the source code for the Morphling UI. 4 | 5 | ## Prerequisites 6 | 7 | - NodeJS > 10 8 | - Go > 1.14 9 | 10 | ## Folder structure 11 | 12 | 1. For React frontend, you can find it under console/frontend. 13 | 14 | 2. For Go backend you, can find it under console/backend. 15 | 16 | ## Development 17 | 18 | #### Build Console Backend Server 19 | ```bash 20 | go build -mod=vendor -o backend-server morphling/console/backend 21 | ``` 22 | #### Run local Console Backend Server 23 | 24 | 1. Prepare a `kubeconfig` file which defines k8s development environment. 25 | 2. Set `KUBECONFIG` environment variable. 26 | ```bash 27 | export KUBECONFIG={/path-to-kubeconfig-file} 28 | ``` 29 | 30 | 31 | ### Serve UI frontend 32 | ```bash 33 | cd console/frontend/ 34 | ``` 35 | 1. Install dependencies (optional) 36 | ```bash 37 | npm install 38 | ``` 39 | 40 | 2. Run `npm run build` under `/frontend` folder. It will create `/frontend/dist` directory with optimized production build. 41 | 42 | 2. Go to `console/backend/`. 43 | 44 | 3. Run backend server with disabled authentication mode 45 | ```bash 46 | ./backend-server 47 | ``` 48 | 49 | After that, you can access the UI using this URL: `http://localhost:9091`. 50 | 51 | #### Optional: Start Console Frontend with Connection to other dev Backend-Server directly 52 | If you are not able to run local console backend server, or other dev console backend server is already present, you could make frontend dev server to proxy API requests to other dev backend server directly. 53 | 54 | 1. Change Proxy Backend 55 | Path: console/frontend/config/config.js 56 | ```javascript 57 | proxy: [ 58 | { 59 | target: "http://localhost:9091", 60 | ... 61 | } 62 | ] 63 | ``` 64 | change the target to address of other present console backend server. 65 | 66 | 2. Run Console Frontend Dev Server 67 | ```bash 68 | npm run start 69 | ``` 70 | 71 | ## Code style 72 | 73 | Our UI is built upon [Ant Design](https://ant.design/). 74 | 75 | 76 | -------------------------------------------------------------------------------- /console/backend/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/alibaba/morphling/console/backend/pkg/client" 6 | "github.com/alibaba/morphling/console/backend/pkg/routers" 7 | ) 8 | 9 | var ( 10 | port, host, buildDir *string 11 | ) 12 | 13 | func init() { 14 | port = flag.String("port", "9091", "the port to listen to for incoming HTTP connections") 15 | host = flag.String("host", "0.0.0.0", "the host to listen to for incoming HTTP connections") 16 | buildDir = flag.String("build-dir", "dist", "the dir of frontend") 17 | } 18 | 19 | func main() { 20 | flag.Parse() 21 | 22 | cmgr := client.Init() 23 | 24 | // r: 25 | r := routers.InitRouter(cmgr) 26 | client.Start() 27 | // r: 28 | _ = r.Run(":9091") 29 | } 30 | -------------------------------------------------------------------------------- /console/backend/pkg/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | 6 | morphlingv1alpha1 "github.com/alibaba/morphling/api/v1alpha1" 7 | "k8s.io/apimachinery/pkg/runtime" 8 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 9 | "k8s.io/client-go/rest" 10 | "k8s.io/klog" 11 | ctrl "sigs.k8s.io/controller-runtime" 12 | "sigs.k8s.io/controller-runtime/pkg/cache" 13 | "sigs.k8s.io/controller-runtime/pkg/client" 14 | ) 15 | 16 | var ( 17 | setupLog = ctrl.Log.WithName("setup") 18 | cmgr = &ClientMgr{} 19 | scheme = runtime.NewScheme() 20 | ) 21 | 22 | type ClientMgr struct { 23 | config *rest.Config 24 | scheme *runtime.Scheme 25 | ctrlCache cache.Cache 26 | ctrlClient client.Client 27 | //kubeClient clientset.Interface 28 | } 29 | 30 | func Init() *ClientMgr { 31 | cmgr.config = ctrl.GetConfigOrDie() 32 | cmgr.scheme = scheme 33 | _ = clientgoscheme.AddToScheme(cmgr.scheme) 34 | _ = morphlingv1alpha1.AddToScheme(cmgr.scheme) 35 | 36 | ctrlCache, err := cache.New(cmgr.config, cache.Options{Scheme: cmgr.scheme}) 37 | if err != nil { 38 | klog.Fatal(err) 39 | } 40 | cmgr.ctrlCache = ctrlCache 41 | 42 | c, err := client.New(cmgr.config, client.Options{Scheme: cmgr.scheme}) 43 | if err != nil { 44 | klog.Fatal(err) 45 | } 46 | 47 | cmgr.ctrlClient = &client.DelegatingClient{ 48 | Reader: &client.DelegatingReader{ 49 | CacheReader: ctrlCache, 50 | ClientReader: c, 51 | }, 52 | Writer: c, 53 | StatusClient: c, 54 | } 55 | return cmgr 56 | } 57 | 58 | func Start() { 59 | go func() { 60 | stopChan := make(chan struct{}) 61 | cmgr.ctrlCache.Start(stopChan) 62 | }() 63 | } 64 | 65 | func (c *ClientMgr) GetCtrlClient() client.Client { 66 | return c.ctrlClient 67 | } 68 | 69 | // IndexField is Used for filtering Pods from PodList 70 | func (c *ClientMgr) IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error { 71 | return c.ctrlCache.IndexField(context.Background(), obj, field, extractValue) 72 | } 73 | -------------------------------------------------------------------------------- /console/backend/pkg/constant/constant.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import "os" 4 | 5 | const ApiV1Routes = "/api/v1alpha1" 6 | 7 | const ( 8 | ResourceGPU = "nvidia.com/gpu" 9 | IndexNodeName = "spec.nodeName" 10 | IndexPhase = "status.phase" 11 | GPUType = "aliyun.accelerator/nvidia_name" 12 | ) 13 | 14 | var ( 15 | PreservedNS = [...]string{"kube-system", "kube-public"} 16 | DefaultUINamespace = GetEnvOrDefault("MORPHLING_UI_NAMESPACE", "morphling-system") 17 | ) 18 | 19 | const ( 20 | DefaultPeId = "pe-1234" 21 | DefaultUserId = "user-1234" 22 | DefaultUserName = "user-abcd" 23 | ) 24 | 25 | const ( 26 | JobInfoTimeFormat = "2006-01-02 15:04:05" 27 | ) 28 | 29 | func GetEnvOrDefault(key string, fallback string) string { 30 | if value, ok := os.LookupEnv(key); ok { 31 | return value 32 | } 33 | return fallback 34 | } 35 | -------------------------------------------------------------------------------- /console/backend/pkg/routers/api/common.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/alibaba/morphling/console/backend/pkg/utils" 5 | "github.com/gin-gonic/gin" 6 | "k8s.io/klog" 7 | ) 8 | 9 | func handleErr(c *gin.Context, msg string) { 10 | formattedMsg := msg 11 | klog.Error(formattedMsg) 12 | utils.Failed(c, msg) 13 | } 14 | 15 | func pushToGitHub(filePath string, content []byte, gitHubRepoInfo utils.GitHubRepoInfo) error { 16 | // Setup GitHub client 17 | ctx := context.Background() 18 | 19 | client := github.NewClient(oauth2.NewClient(ctx, oauth2.StaticTokenSource( 20 | &oauth2.Token{AccessToken: gitHubRepoInfo.AccessToken}, 21 | ))) 22 | 23 | // Check if file exists 24 | fileContent, _, _, err := client.Repositories.GetContents( 25 | ctx, 26 | gitHubRepoInfo.Owner, 27 | gitHubRepoInfo.Repo, 28 | filePath, 29 | &github.RepositoryContentGetOptions{Ref: gitHubRepoInfo.Branch}, 30 | ) 31 | if err != nil && !strings.Contains(err.Error(), "404") { 32 | klog.Errorf("error checking file existence: %v", err) 33 | return err 34 | } 35 | 36 | // Prepare commit message 37 | commitMessage := fmt.Sprintf("Update %s", filePath) 38 | var sha *string 39 | if fileContent != nil { 40 | sha = fileContent.SHA 41 | } 42 | 43 | // Create or update file 44 | _, _, err = client.Repositories.CreateFile(ctx, gitHubRepoInfo.Owner, gitHubRepoInfo.Repo, filePath, &github.RepositoryContentFileOptions{ 45 | Message: &commitMessage, 46 | Content: content, 47 | SHA: sha, 48 | Branch: &gitHubRepoInfo.Branch, 49 | }) 50 | 51 | if err != nil { 52 | klog.Errorf("error pushing file to GitHub: %v", err) 53 | return err 54 | } 55 | 56 | return nil 57 | } -------------------------------------------------------------------------------- /console/backend/pkg/routers/api/llm_service_version.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/alibaba/morphling/console/backend/pkg/handlers" 7 | "github.com/alibaba/morphling/console/backend/pkg/utils" 8 | "github.com/gin-gonic/gin" 9 | "k8s.io/klog" 10 | ) 11 | 12 | func NewLLMServiceVersionAPIsController(llmServiceVersionHandler *handlers.LLMServiceVersionHandler) *LLMServiceVersionAPIsController { 13 | return &LLMServiceVersionAPIsController{ 14 | llmServiceVersionHandler: llmServiceVersionHandler, 15 | } 16 | } 17 | 18 | type LLMServiceVersionAPIsController struct { 19 | llmServiceVersionHandler *handlers.LLMServiceVersionHandler 20 | } 21 | 22 | func (ctrl *LLMServiceVersionAPIsController) RegisterRoutes(routes *gin.RouterGroup) { 23 | llmServiceVersion := routes.Group("/llm-service-version") 24 | llmServiceVersion.POST("", ctrl.createLLMServiceVersion) 25 | llmServiceVersion.GET("", ctrl.getLLMServiceVersions) 26 | } 27 | 28 | func (ctrl *LLMServiceVersionAPIsController) createLLMServiceVersion(c *gin.Context) { 29 | data, err := c.GetRawData() 30 | if err != nil { 31 | handleErr(c, fmt.Sprintf("failed to get raw posted data from request")) 32 | return 33 | } 34 | 35 | klog.Infof("") 36 | 37 | // Unmarshal the data to LLMServiceVersion and github repo info 38 | var llmServiceVersionRequest utils.LLMServiceVersionRequest 39 | err = json.Unmarshal(data, &llmServiceVersionRequest) 40 | if err != nil { 41 | handleErr(c, fmt.Sprintf("failed to unmarshal llmServiceVersionRequest in json format")) 42 | return 43 | } 44 | 45 | klog.Infof("Received LLMServiceVersionRequest: %+v", llmServiceVersionRequest) 46 | 47 | if err := ctrl.llmServiceVersionHandler.CreateLLMServiceVersion(&llmServiceVersionRequest); err != nil { 48 | handleErr(c, fmt.Sprintf("Failed to create LLM service version: %v", err)) 49 | return 50 | } 51 | 52 | utils.Succeed(c, nil) 53 | } 54 | 55 | func (ctrl *LLMServiceVersionAPIsController) getLLMServiceVersions(c *gin.Context) { 56 | // todo 57 | 58 | utils.Succeed(c, nil) 59 | } 60 | -------------------------------------------------------------------------------- /console/backend/pkg/utils/redirects.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | func Redirect404(c *gin.Context) { 9 | c.Redirect(http.StatusFound, "/404") 10 | } 11 | 12 | func Redirect403(c *gin.Context) { 13 | c.Redirect(http.StatusFound, "/403") 14 | } 15 | 16 | func Redirect500(c *gin.Context) { 17 | c.Redirect(http.StatusFound, "/500") 18 | } 19 | -------------------------------------------------------------------------------- /console/frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /console/frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | /lambda/ 2 | /scripts 3 | /config 4 | .history 5 | public 6 | dist 7 | .umi 8 | mock -------------------------------------------------------------------------------- /console/frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [require.resolve('@umijs/fabric/dist/eslint')], 3 | globals: { 4 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, 5 | page: true, 6 | REACT_APP_ENV: true, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /console/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | **/node_modules 5 | # roadhog-api-doc ignore 6 | /src/utils/request-temp.js 7 | _roadhog-api-doc 8 | 9 | # production 10 | /dist 11 | /.vscode 12 | 13 | # misc 14 | .DS_Store 15 | npm-debug.log* 16 | yarn-error.log 17 | 18 | /coverage 19 | .idea 20 | yarn.lock 21 | package-lock.json 22 | *bak 23 | .vscode 24 | 25 | # visual studio code 26 | .history 27 | *.log 28 | functions/* 29 | .temp/** 30 | 31 | # umi 32 | .umi 33 | .umi-production 34 | 35 | # screenshot 36 | screenshot 37 | .firebase 38 | .eslintcache 39 | 40 | build 41 | -------------------------------------------------------------------------------- /console/frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.svg 2 | package.json 3 | .umi 4 | .umi-production 5 | /dist 6 | .dockerignore 7 | .DS_Store 8 | .eslintignore 9 | *.png 10 | *.toml 11 | docker 12 | .editorconfig 13 | Dockerfile* 14 | .gitignore 15 | .prettierignore 16 | LICENSE 17 | .eslintcache 18 | *.lock 19 | yarn-error.log 20 | .history 21 | CNAME 22 | /build 23 | /public -------------------------------------------------------------------------------- /console/frontend/.prettierrc.js: -------------------------------------------------------------------------------- 1 | const fabric = require('@umijs/fabric'); 2 | 3 | module.exports = { 4 | ...fabric.prettier, 5 | }; 6 | -------------------------------------------------------------------------------- /console/frontend/.stylelintrc.js: -------------------------------------------------------------------------------- 1 | const fabric = require('@umijs/fabric'); 2 | 3 | module.exports = { 4 | ...fabric.stylelint, 5 | }; 6 | -------------------------------------------------------------------------------- /console/frontend/README.md: -------------------------------------------------------------------------------- 1 | # Ant Design Pro 2 | 3 | This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use. 4 | 5 | ## Environment Prepare 6 | 7 | Install `node_modules`: 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | or 14 | 15 | ```bash 16 | yarn 17 | ``` 18 | 19 | ## Provided Scripts 20 | 21 | Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test. 22 | 23 | Scripts provided in `package.json`. It's safe to modify or add additional script: 24 | 25 | ### Start project 26 | 27 | ```bash 28 | npm start 29 | ``` 30 | 31 | ### Build project 32 | 33 | ```bash 34 | npm run build 35 | ``` 36 | 37 | ### Check code style 38 | 39 | ```bash 40 | npm run lint 41 | ``` 42 | 43 | You can also use script to auto fix some lint error: 44 | 45 | ```bash 46 | npm run lint:fix 47 | ``` 48 | 49 | ### Test code 50 | 51 | ```bash 52 | npm test 53 | ``` 54 | 55 | ## More 56 | 57 | You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro). 58 | -------------------------------------------------------------------------------- /console/frontend/config/config.dev.js: -------------------------------------------------------------------------------- 1 | // https://umijs.org/config/ 2 | import {defineConfig} from 'umi'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | // https://github.com/zthxxx/react-dev-inspector 7 | 'react-dev-inspector/plugins/umi/react-inspector', 8 | ], 9 | // https://github.com/zthxxx/react-dev-inspector#inspector-loader-props 10 | inspectorConfig: { 11 | exclude: [], 12 | babelPlugins: [], 13 | babelOptions: {}, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /console/frontend/config/config.js: -------------------------------------------------------------------------------- 1 | // https://umijs.org/config/ 2 | import {defineConfig} from 'umi'; 3 | import defaultSettings from './defaultSettings'; 4 | import routes from './routes'; 5 | 6 | const {REACT_APP_ENV} = process.env; 7 | export default defineConfig({ 8 | hash: true, 9 | antd: {}, 10 | dva: { 11 | hmr: true, 12 | }, 13 | history: { 14 | type: 'browser', 15 | }, 16 | locale: { 17 | // default zh-CN 18 | default: 'zh-CN', 19 | antd: true, 20 | // default true, when it is true, will use `navigator.language` overwrite default 21 | baseNavigator: true, 22 | }, 23 | dynamicImport: { 24 | loading: '@/components/PageLoading/index', 25 | }, 26 | targets: { 27 | ie: 11, 28 | }, 29 | // umi routers: https://umijs.org/docs/routing 30 | routes, 31 | // Theme for antd: https://ant.design/docs/react/customize-theme-cn 32 | theme: { 33 | 'primary-color': defaultSettings.primaryColor, 34 | }, 35 | title: false, 36 | ignoreMomentLocale: true, 37 | // proxy: proxy[REACT_APP_ENV || 'dev'], 38 | proxy: { 39 | '/api/v1alpha1': { 40 | target: 'http://localhost:9091', 41 | changeOrigin: true, 42 | context: function (pathname, req) { 43 | // 对于登录后自动 302 的,不会带上 Accept header 但是也需要进行代理 44 | if (pathname.startsWith('/sendBucSSOToken.do')) { 45 | return true; 46 | } 47 | 48 | return req.headers['accept'] === 'application/json'; 49 | }, 50 | }, 51 | }, 52 | manifest: { 53 | basePath: '/', 54 | }, 55 | // 快速刷新功能 https://umijs.org/config#fastrefresh 56 | fastRefresh: {}, 57 | esbuild: {}, 58 | webpack5: {}, 59 | mfsu: {}, 60 | }); 61 | -------------------------------------------------------------------------------- /console/frontend/config/defaultSettings.js: -------------------------------------------------------------------------------- 1 | const proSettings = { 2 | navTheme: 'dark', 3 | // 拂晓蓝 4 | primaryColor: '#1890ff', 5 | layout: 'side', 6 | contentWidth: 'Fluid', 7 | fixedHeader: false, 8 | fixSiderbar: true, 9 | colorWeak: false, 10 | title: 'Morphling', 11 | pwa: false, 12 | iconfontUrl: '', 13 | }; 14 | export default proSettings; 15 | -------------------------------------------------------------------------------- /console/frontend/config/proxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置 3 | * ------------------------------- 4 | * The agent cannot take effect in the production environment 5 | * so there is no configuration of the production environment 6 | * For details, please see 7 | * https://pro.ant.design/docs/deploy 8 | */ 9 | export default { 10 | dev: { 11 | '/api/': { 12 | target: 'https://preview.pro.ant.design', 13 | changeOrigin: true, 14 | pathRewrite: { 15 | '^': '', 16 | }, 17 | }, 18 | }, 19 | test: { 20 | '/api/': { 21 | target: 'https://preview.pro.ant.design', 22 | changeOrigin: true, 23 | pathRewrite: { 24 | '^': '', 25 | }, 26 | }, 27 | }, 28 | pre: { 29 | '/api/': { 30 | target: 'your pre url', 31 | changeOrigin: true, 32 | pathRewrite: { 33 | '^': '', 34 | }, 35 | }, 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /console/frontend/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testURL: 'http://localhost:8000', 3 | testEnvironment: './tests/PuppeteerEnvironment', 4 | verbose: false, 5 | globals: { 6 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false, 7 | localStorage: null, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /console/frontend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": [ 8 | "./src/*" 9 | ] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /console/frontend/mock/llmServiceVersion.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'GET /api/v1alpha1/llm-service-version': (req, res) => { 3 | res.send({ 4 | code: '200', 5 | data: [ 6 | { 7 | id: 1, 8 | modelName: 'microsoft/phi-2', 9 | version: 'v1.0.0', 10 | status: 'COMPLETED', 11 | createdAt: '2024-03-20 10:00:00', 12 | testedAt: '2024-03-20 12:00:00', 13 | objectiveMetricName: 'qps', 14 | bestValue: 0.98 15 | }, 16 | { 17 | id: 2, 18 | modelName: 'GPT2', 19 | version: 'v1.0.0', 20 | status: 'PENDING', 21 | createdAt: '2024-03-20 10:00:00', 22 | objectiveMetricName: 'qps', 23 | } 24 | ], 25 | }); 26 | }, 27 | }; -------------------------------------------------------------------------------- /console/frontend/mock/route.js: -------------------------------------------------------------------------------- 1 | export default { 2 | '/api/auth_routes': { 3 | '/form/advanced-form': { 4 | authority: ['admin', 'user'], 5 | }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /console/frontend/public/CNAME: -------------------------------------------------------------------------------- 1 | preview.pro.ant.design -------------------------------------------------------------------------------- /console/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubedl-io/morphling/764b39cb6e9546b0224df25e7699890e8342dd78/console/frontend/public/favicon.ico -------------------------------------------------------------------------------- /console/frontend/public/home_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubedl-io/morphling/764b39cb6e9546b0224df25e7699890e8342dd78/console/frontend/public/home_bg.png -------------------------------------------------------------------------------- /console/frontend/public/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubedl-io/morphling/764b39cb6e9546b0224df25e7699890e8342dd78/console/frontend/public/icons/icon-128x128.png -------------------------------------------------------------------------------- /console/frontend/public/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubedl-io/morphling/764b39cb6e9546b0224df25e7699890e8342dd78/console/frontend/public/icons/icon-192x192.png -------------------------------------------------------------------------------- /console/frontend/public/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubedl-io/morphling/764b39cb6e9546b0224df25e7699890e8342dd78/console/frontend/public/icons/icon-512x512.png -------------------------------------------------------------------------------- /console/frontend/public/pro_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubedl-io/morphling/764b39cb6e9546b0224df25e7699890e8342dd78/console/frontend/public/pro_icon.png -------------------------------------------------------------------------------- /console/frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubedl-io/morphling/764b39cb6e9546b0224df25e7699890e8342dd78/console/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /console/frontend/src/components/Authorized/Authorized.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Result} from 'antd'; 3 | import check from './CheckPermissions'; 4 | 5 | const Authorized = ({ 6 | children, 7 | authority, 8 | noMatch = ( 9 | 14 | ), 15 | }) => { 16 | const childrenRender = typeof children === 'undefined' ? null : children; 17 | const dom = check(authority, childrenRender, noMatch); 18 | return <>{dom}; 19 | }; 20 | 21 | export default Authorized; 22 | -------------------------------------------------------------------------------- /console/frontend/src/components/Authorized/AuthorizedRoute.jsx: -------------------------------------------------------------------------------- 1 | import {Redirect, Route} from 'umi'; 2 | import React from 'react'; 3 | import Authorized from './Authorized'; 4 | 5 | const AuthorizedRoute = ({component: Component, render, authority, redirectPath, ...rest}) => ( 6 | ( 12 | 17 | )} 18 | /> 19 | } 20 | > 21 | (Component ? : render(props))}/> 22 | 23 | ); 24 | 25 | export default AuthorizedRoute; 26 | -------------------------------------------------------------------------------- /console/frontend/src/components/Authorized/index.jsx: -------------------------------------------------------------------------------- 1 | import Authorized from './Authorized'; 2 | import Secured from './Secured'; 3 | import check from './CheckPermissions'; 4 | import renderAuthorize from './renderAuthorize'; 5 | 6 | Authorized.Secured = Secured; 7 | Authorized.check = check; 8 | const RenderAuthorize = renderAuthorize(Authorized); 9 | export default RenderAuthorize; 10 | -------------------------------------------------------------------------------- /console/frontend/src/components/Authorized/renderAuthorize.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable eslint-comments/disable-enable-pair */ 2 | 3 | /* eslint-disable import/no-mutable-exports */ 4 | let CURRENT = 'NULL'; 5 | 6 | /** 7 | * Use authority or getAuthority 8 | * 9 | * @param {string|()=>String} currentAuthority 10 | */ 11 | const renderAuthorize = (Authorized) => (currentAuthority) => { 12 | if (currentAuthority) { 13 | if (typeof currentAuthority === 'function') { 14 | CURRENT = currentAuthority(); 15 | } 16 | 17 | if ( 18 | Object.prototype.toString.call(currentAuthority) === '[object String]' || 19 | Array.isArray(currentAuthority) 20 | ) { 21 | CURRENT = currentAuthority; 22 | } 23 | } else { 24 | CURRENT = 'NULL'; 25 | } 26 | 27 | return Authorized; 28 | }; 29 | 30 | export {CURRENT}; 31 | export default (Authorized) => renderAuthorize(Authorized); 32 | -------------------------------------------------------------------------------- /console/frontend/src/components/ExperimentStatus/index.js: -------------------------------------------------------------------------------- 1 | import {Badge} from 'antd'; 2 | import React from 'react'; 3 | import {FormattedMessage} from 'umi'; 4 | 5 | const STATUS_MAP = { 6 | 'All': { 7 | text: , 8 | // text: 'All', 9 | status: 'default', 10 | }, 11 | 'Created': { 12 | text: , 13 | // text: 'Created', 14 | status: 'processing', 15 | }, 16 | 'Waiting': { 17 | text: , 18 | // text: 'Waiting', 19 | status: 'processing', 20 | }, 21 | 'Running': { 22 | text: , 23 | // text: 'Running', 24 | status: 'processing', 25 | }, 26 | 'Succeeded': { 27 | text: , 28 | // text: 'Succeeded', 29 | status: 'success', 30 | }, 31 | 'Failed': { 32 | text: , 33 | // text: 'Failed', 34 | status: 'error', 35 | }, 36 | 'Stopped': { 37 | text: , 38 | // text: 'Stopped', 39 | status: 'error', 40 | }, 41 | } 42 | 43 | const ExperimentStatus = props => { 44 | const {status} = props 45 | 46 | const s = STATUS_MAP[status] || { 47 | text: , 48 | // text: 'Stopped', 49 | status: 'default', 50 | } 51 | 52 | return ( 53 | 54 | ) 55 | 56 | }; 57 | 58 | export default ExperimentStatus; 59 | -------------------------------------------------------------------------------- /console/frontend/src/components/GlobalHeader/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | @pro-header-hover-bg: rgba(0, 0, 0, 0.025); 4 | 5 | .menu { 6 | :global(.anticon) { 7 | margin-right: 8px; 8 | } 9 | 10 | :global(.ant-dropdown-menu-item) { 11 | min-width: 160px; 12 | } 13 | } 14 | 15 | .right { 16 | display: flex; 17 | float: right; 18 | height: 48px; 19 | margin-left: auto; 20 | overflow: hidden; 21 | 22 | .action { 23 | display: flex; 24 | align-items: center; 25 | height: 100%; 26 | padding: 0 12px; 27 | cursor: pointer; 28 | transition: all 0.3s; 29 | 30 | > span { 31 | vertical-align: middle; 32 | } 33 | 34 | &:hover { 35 | background: @pro-header-hover-bg; 36 | } 37 | 38 | &:global(.opened) { 39 | background: @pro-header-hover-bg; 40 | } 41 | } 42 | 43 | .search { 44 | padding: 0 12px; 45 | 46 | &:hover { 47 | background: transparent; 48 | } 49 | } 50 | 51 | .account { 52 | .avatar { 53 | margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0; 54 | margin-right: 8px; 55 | color: @primary-color; 56 | vertical-align: top; 57 | background: rgba(255, 255, 255, 0.85); 58 | } 59 | } 60 | } 61 | 62 | .dark { 63 | .action { 64 | color: rgba(255, 255, 255, 0.85); 65 | 66 | > span { 67 | color: rgba(255, 255, 255, 0.85); 68 | } 69 | 70 | &:hover, 71 | &:global(.opened) { 72 | background: @primary-color; 73 | } 74 | } 75 | } 76 | 77 | :global(.ant-pro-global-header) { 78 | .dark { 79 | .action { 80 | color: @text-color; 81 | 82 | > span { 83 | color: @text-color; 84 | } 85 | 86 | &:hover { 87 | color: rgba(255, 255, 255, 0.85); 88 | 89 | > span { 90 | color: rgba(255, 255, 255, 0.85); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /console/frontend/src/components/HeaderDropdown/index.jsx: -------------------------------------------------------------------------------- 1 | import {Dropdown} from 'antd'; 2 | import React from 'react'; 3 | import classNames from 'classnames'; 4 | import styles from './index.less'; 5 | 6 | const HeaderDropdown = ({overlayClassName: cls, ...restProps}) => ( 7 | 8 | ); 9 | 10 | export default HeaderDropdown; 11 | -------------------------------------------------------------------------------- /console/frontend/src/components/HeaderDropdown/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .container > * { 4 | background-color: @popover-bg; 5 | border-radius: 4px; 6 | box-shadow: @shadow-1-down; 7 | } 8 | 9 | @media screen and (max-width: @screen-xs) { 10 | .container { 11 | width: 100% !important; 12 | } 13 | 14 | .container > * { 15 | border-radius: 0 !important; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /console/frontend/src/components/HeaderSearch/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .headerSearch { 4 | .input { 5 | width: 0; 6 | min-width: 0; 7 | overflow: hidden; 8 | background: transparent; 9 | border-radius: 0; 10 | transition: width 0.3s, margin-left 0.3s; 11 | 12 | :global(.ant-select-selection) { 13 | background: transparent; 14 | } 15 | 16 | input { 17 | padding-right: 0; 18 | padding-left: 0; 19 | border: 0; 20 | box-shadow: none !important; 21 | } 22 | 23 | &, 24 | &:hover, 25 | &:focus { 26 | border-bottom: 1px solid @border-color-base; 27 | } 28 | 29 | &.show { 30 | width: 210px; 31 | margin-left: 8px; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /console/frontend/src/components/NoticeIcon/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .popover { 4 | position: relative; 5 | width: 336px; 6 | } 7 | 8 | .noticeButton { 9 | display: inline-block; 10 | cursor: pointer; 11 | transition: all 0.3s; 12 | } 13 | 14 | .icon { 15 | padding: 4px; 16 | vertical-align: middle; 17 | } 18 | 19 | .badge { 20 | font-size: 16px; 21 | } 22 | 23 | .tabs { 24 | :global { 25 | .ant-tabs-nav-list { 26 | margin: auto; 27 | } 28 | 29 | .ant-tabs-nav-scroll { 30 | text-align: center; 31 | } 32 | 33 | .ant-tabs-bar { 34 | margin-bottom: 0; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /console/frontend/src/components/PageLoading/index.jsx: -------------------------------------------------------------------------------- 1 | import {PageLoading} from '@ant-design/pro-layout'; // loading components from code split 2 | // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport 3 | 4 | export default PageLoading; 5 | -------------------------------------------------------------------------------- /console/frontend/src/components/PodStatus/index.js: -------------------------------------------------------------------------------- 1 | import {Badge} from 'antd'; 2 | import React from 'react'; 3 | import ExperimentStatus from "@/components/ExperimentStatus"; 4 | 5 | const STATUS_MAP = { 6 | 'All': { 7 | text: '全部', 8 | // text: 'All', 9 | status: 'default', 10 | }, 11 | 'Completed': { 12 | text: '已完成', 13 | // text: 'Created', 14 | status: 'default', 15 | }, 16 | 'Pending': { 17 | text: '创建中', 18 | // text: 'Waiting', 19 | status: 'processing', 20 | }, 21 | 'Running': { 22 | text: '运行中', 23 | // text: 'Running', 24 | status: 'success', 25 | }, 26 | 'Failed': { 27 | text: '执行失败', 28 | // text: 'Failed', 29 | status: 'error', 30 | }, 31 | 'Stopped': { 32 | text: '已停止', 33 | // text: 'Stopped', 34 | status: 'error', 35 | }, 36 | } 37 | 38 | const PodStatus = props => { 39 | const {status} = props 40 | const s = STATUS_MAP[status] || { 41 | text: '未知', 42 | // text: 'Stopped', 43 | status: 'default', 44 | } 45 | 46 | return ( 47 | 48 | ) 49 | 50 | }; 51 | 52 | export default ExperimentStatus; 53 | -------------------------------------------------------------------------------- /console/frontend/src/e2e/__mocks__/antd-pro-merge-less.js: -------------------------------------------------------------------------------- 1 | export default undefined; 2 | -------------------------------------------------------------------------------- /console/frontend/src/e2e/baseLayout.e2e.js: -------------------------------------------------------------------------------- 1 | const {uniq} = require('lodash'); 2 | const RouterConfig = require('../../config/config').default.routes; 3 | 4 | const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; 5 | 6 | function formatter(routes, parentPath = '') { 7 | const fixedParentPath = parentPath.replace(/\/{1,}/g, '/'); 8 | let result = []; 9 | routes.forEach((item) => { 10 | if (item.path) { 11 | result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/')); 12 | } 13 | if (item.routes) { 14 | result = result.concat( 15 | formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath), 16 | ); 17 | } 18 | }); 19 | return uniq(result.filter((item) => !!item)); 20 | } 21 | 22 | beforeEach(async () => { 23 | await page.goto(`${BASE_URL}`); 24 | await page.evaluate(() => { 25 | localStorage.setItem('antd-pro-authority', '["admin"]'); 26 | }); 27 | }); 28 | 29 | describe('Morphling E2E test', () => { 30 | const testPage = (path) => async () => { 31 | await page.goto(`${BASE_URL}${path}`); 32 | await page.waitForSelector('footer', { 33 | timeout: 2000, 34 | }); 35 | const haveFooter = await page.evaluate( 36 | () => document.getElementsByTagName('footer').length > 0, 37 | ); 38 | expect(haveFooter).toBeTruthy(); 39 | }; 40 | 41 | const routers = formatter(RouterConfig); 42 | routers.forEach((route) => { 43 | it(`test pages ${route}`, testPage(route)); 44 | }); 45 | 46 | it('topmenu should have footer', async () => { 47 | const params = '?navTheme=light&layout=topmenu'; 48 | await page.goto(`${BASE_URL}${params}`); 49 | await page.waitForSelector('footer', { 50 | timeout: 2000, 51 | }); 52 | const haveFooter = await page.evaluate( 53 | () => document.getElementsByTagName('footer').length > 0, 54 | ); 55 | expect(haveFooter).toBeTruthy(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /console/frontend/src/global.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | html, 4 | body, 5 | #root { 6 | height: 100%; 7 | } 8 | 9 | .colorWeak { 10 | filter: invert(80%); 11 | } 12 | 13 | .ant-layout { 14 | min-height: 100vh; 15 | } 16 | 17 | canvas { 18 | display: block; 19 | } 20 | 21 | body { 22 | text-rendering: optimizeLegibility; 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | ul, 28 | ol { 29 | list-style: none; 30 | } 31 | 32 | @media (max-width: @screen-xs) { 33 | .ant-table { 34 | width: 100%; 35 | overflow-x: auto; 36 | 37 | &-thead > tr, 38 | &-tbody > tr { 39 | > th, 40 | > td { 41 | white-space: pre; 42 | 43 | > span { 44 | display: block; 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | // Compatible with IE11 52 | @media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) { 53 | body .ant-design-pro > .ant-layout { 54 | min-height: 100vh; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /console/frontend/src/layouts/BlankLayout.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Inspector} from 'react-dev-inspector'; 3 | 4 | const InspectorWrapper = process.env.NODE_ENV === 'development' ? Inspector : React.Fragment; 5 | 6 | const Layout = ({children}) => { 7 | return {children}; 8 | }; 9 | 10 | export default Layout; 11 | -------------------------------------------------------------------------------- /console/frontend/src/layouts/SecurityLayout.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {PageLoading} from '@ant-design/pro-layout'; 3 | import {connect} from 'umi'; 4 | 5 | class SecurityLayout extends React.Component { 6 | state = { 7 | isReady: false, 8 | }; 9 | 10 | componentDidMount() { 11 | this.setState({ 12 | isReady: true, 13 | }); 14 | const {dispatch} = this.props; 15 | 16 | if (dispatch) { 17 | dispatch({ 18 | type: 'user/fetchCurrent', 19 | }); 20 | } 21 | } 22 | 23 | render() { 24 | const {isReady} = this.state; 25 | const {children, loading, currentUser} = this.props; // You can replace it to your authentication rule (such as check token exists) 26 | // You can replace it with your own login authentication rules (such as judging whether the token exists) 27 | 28 | const isLogin = currentUser && currentUser.accountId; 29 | // const queryString = stringify({ 30 | // redirect: window.location.href, 31 | // }); 32 | 33 | if ((!isLogin && loading) || !isReady) { 34 | return ; 35 | } 36 | 37 | // if (!isLogin && window.location.pathname !== '/user/login') { 38 | // return ; 39 | // } 40 | 41 | return children; 42 | } 43 | } 44 | 45 | export default connect(({user, loading}) => ({ 46 | currentUser: user.currentUser, 47 | loading: loading.models.user, 48 | }))(SecurityLayout); 49 | -------------------------------------------------------------------------------- /console/frontend/src/layouts/UserLayout.jsx: -------------------------------------------------------------------------------- 1 | import {DefaultFooter, getMenuData, getPageTitle} from '@ant-design/pro-layout'; 2 | import {Helmet, HelmetProvider} from 'react-helmet-async'; 3 | import {connect, FormattedMessage, Link, SelectLang, useIntl} from 'umi'; 4 | import React from 'react'; 5 | import logo from '../assets/logo.svg'; 6 | import styles from './UserLayout.less'; 7 | 8 | const UserLayout = (props) => { 9 | const { 10 | route = { 11 | routes: [], 12 | }, 13 | } = props; 14 | const {routes = []} = route; 15 | const { 16 | children, 17 | location = { 18 | pathname: '', 19 | }, 20 | } = props; 21 | const {formatMessage} = useIntl(); 22 | const {breadcrumb} = getMenuData(routes); 23 | const title = getPageTitle({ 24 | pathname: location.pathname, 25 | formatMessage, 26 | breadcrumb, 27 | ...props, 28 | }); 29 | return ( 30 | 31 | 32 | {title} 33 | 34 | 35 | 36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | logo 45 | Ant Design 46 | 47 |
48 |
49 | 53 |
54 |
55 | {children} 56 |
57 | 58 |
59 |
60 | ); 61 | }; 62 | 63 | export default connect(({settings}) => ({...settings}))(UserLayout); 64 | -------------------------------------------------------------------------------- /console/frontend/src/layouts/UserLayout.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .container { 4 | display: flex; 5 | flex-direction: column; 6 | height: 100vh; 7 | overflow: auto; 8 | background: @layout-body-background; 9 | } 10 | 11 | .lang { 12 | width: 100%; 13 | height: 40px; 14 | line-height: 44px; 15 | text-align: right; 16 | 17 | :global(.ant-dropdown-trigger) { 18 | margin-right: 24px; 19 | } 20 | } 21 | 22 | .content { 23 | flex: 1; 24 | padding: 32px 0; 25 | } 26 | 27 | @media (min-width: @screen-md-min) { 28 | .container { 29 | background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg'); 30 | background-repeat: no-repeat; 31 | background-position: center 110px; 32 | background-size: 100%; 33 | } 34 | 35 | .content { 36 | padding: 32px 0 24px; 37 | } 38 | } 39 | 40 | .top { 41 | text-align: center; 42 | } 43 | 44 | .header { 45 | height: 44px; 46 | line-height: 44px; 47 | 48 | a { 49 | text-decoration: none; 50 | } 51 | } 52 | 53 | .logo { 54 | height: 44px; 55 | margin-right: 16px; 56 | vertical-align: top; 57 | } 58 | 59 | .title { 60 | position: relative; 61 | top: 2px; 62 | color: @heading-color; 63 | font-weight: 600; 64 | font-size: 33px; 65 | font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif; 66 | } 67 | 68 | .desc { 69 | margin-top: 12px; 70 | margin-bottom: 40px; 71 | color: @text-color-secondary; 72 | font-size: @font-size-base; 73 | } 74 | -------------------------------------------------------------------------------- /console/frontend/src/locales/en-US.js: -------------------------------------------------------------------------------- 1 | import component from './en-US/component'; 2 | import globalHeader from './en-US/globalHeader'; 3 | import menu from './en-US/menu'; 4 | import pwa from './en-US/pwa'; 5 | import settingDrawer from './en-US/settingDrawer'; 6 | import settings from './en-US/settings'; 7 | import pages from './en-US/pages'; 8 | 9 | export default { 10 | 'navBar.lang': 'Languages', 11 | 'layout.user.link.help': 'Help', 12 | 'layout.user.link.privacy': 'Privacy', 13 | 'layout.user.link.terms': 'Terms', 14 | 'app.preview.down.block': 'Download this page to your local project', 15 | 'app.welcome.link.fetch-blocks': 'Get all block', 16 | 'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development', 17 | ...globalHeader, 18 | ...menu, 19 | ...settingDrawer, 20 | ...settings, 21 | ...pwa, 22 | ...component, 23 | ...pages, 24 | }; 25 | -------------------------------------------------------------------------------- /console/frontend/src/locales/en-US/component.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.tagSelect.expand': 'Expand', 3 | 'component.tagSelect.collapse': 'Collapse', 4 | 'component.tagSelect.all': 'All', 5 | }; 6 | -------------------------------------------------------------------------------- /console/frontend/src/locales/en-US/globalHeader.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.globalHeader.search': 'Search', 3 | 'component.globalHeader.search.example1': 'Search example 1', 4 | 'component.globalHeader.search.example2': 'Search example 2', 5 | 'component.globalHeader.search.example3': 'Search example 3', 6 | 'component.globalHeader.help': 'Help', 7 | 'component.globalHeader.notification': 'Notification', 8 | 'component.globalHeader.notification.empty': 'You have viewed all notifications.', 9 | 'component.globalHeader.message': 'Message', 10 | 'component.globalHeader.message.empty': 'You have viewed all messsages.', 11 | 'component.globalHeader.event': 'Event', 12 | 'component.globalHeader.event.empty': 'You have viewed all events.', 13 | 'component.noticeIcon.clear': 'Clear', 14 | 'component.noticeIcon.cleared': 'Cleared', 15 | 'component.noticeIcon.empty': 'No notifications', 16 | 'component.noticeIcon.view-more': 'View more', 17 | }; 18 | -------------------------------------------------------------------------------- /console/frontend/src/locales/en-US/pwa.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': 'You are offline now', 3 | 'app.pwa.serviceworker.updated': 'New content is available', 4 | 'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page', 5 | 'app.pwa.serviceworker.updated.ok': 'Refresh', 6 | }; 7 | -------------------------------------------------------------------------------- /console/frontend/src/locales/en-US/settingDrawer.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.setting.pagestyle': 'Page style setting', 3 | 'app.setting.pagestyle.dark': 'Dark style', 4 | 'app.setting.pagestyle.light': 'Light style', 5 | 'app.setting.content-width': 'Content Width', 6 | 'app.setting.content-width.fixed': 'Fixed', 7 | 'app.setting.content-width.fluid': 'Fluid', 8 | 'app.setting.themecolor': 'Theme Color', 9 | 'app.setting.themecolor.dust': 'Dust Red', 10 | 'app.setting.themecolor.volcano': 'Volcano', 11 | 'app.setting.themecolor.sunset': 'Sunset Orange', 12 | 'app.setting.themecolor.cyan': 'Cyan', 13 | 'app.setting.themecolor.green': 'Polar Green', 14 | 'app.setting.themecolor.daybreak': 'Daybreak Blue (default)', 15 | 'app.setting.themecolor.geekblue': 'Geek Glue', 16 | 'app.setting.themecolor.purple': 'Golden Purple', 17 | 'app.setting.navigationmode': 'Navigation Mode', 18 | 'app.setting.sidemenu': 'Side Menu Layout', 19 | 'app.setting.topmenu': 'Top Menu Layout', 20 | 'app.setting.fixedheader': 'Fixed Header', 21 | 'app.setting.fixedsidebar': 'Fixed Sidebar', 22 | 'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout', 23 | 'app.setting.hideheader': 'Hidden Header when scrolling', 24 | 'app.setting.hideheader.hint': 'Works when Hidden Header is enabled', 25 | 'app.setting.othersettings': 'Other Settings', 26 | 'app.setting.weakmode': 'Weak Mode', 27 | 'app.setting.copy': 'Copy Setting', 28 | 'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js', 29 | 'app.setting.production.hint': 30 | 'Setting panel shows in development environment only, please manually modify', 31 | }; 32 | -------------------------------------------------------------------------------- /console/frontend/src/locales/zh-CN.js: -------------------------------------------------------------------------------- 1 | import component from './zh-CN/component'; 2 | import globalHeader from './zh-CN/globalHeader'; 3 | import menu from './zh-CN/menu'; 4 | import pwa from './zh-CN/pwa'; 5 | import settingDrawer from './zh-CN/settingDrawer'; 6 | import settings from './zh-CN/settings'; 7 | import pages from './zh-CN/pages'; 8 | 9 | export default { 10 | 'navBar.lang': '语言', 11 | 'layout.user.link.help': '帮助', 12 | 'layout.user.link.privacy': '隐私', 13 | 'layout.user.link.terms': '条款', 14 | 'app.preview.down.block': '下载此页面到本地项目', 15 | 'app.welcome.link.fetch-blocks': '获取全部区块', 16 | 'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面', 17 | ...pages, 18 | ...globalHeader, 19 | ...menu, 20 | ...settingDrawer, 21 | ...settings, 22 | ...pwa, 23 | ...component, 24 | }; 25 | -------------------------------------------------------------------------------- /console/frontend/src/locales/zh-CN/component.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.tagSelect.expand': '展开', 3 | 'component.tagSelect.collapse': '收起', 4 | 'component.tagSelect.all': '全部', 5 | }; 6 | -------------------------------------------------------------------------------- /console/frontend/src/locales/zh-CN/globalHeader.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.globalHeader.search': '站内搜索', 3 | 'component.globalHeader.search.example1': '搜索提示一', 4 | 'component.globalHeader.search.example2': '搜索提示二', 5 | 'component.globalHeader.search.example3': '搜索提示三', 6 | 'component.globalHeader.help': '使用文档', 7 | 'component.globalHeader.notification': '通知', 8 | 'component.globalHeader.notification.empty': '你已查看所有通知', 9 | 'component.globalHeader.message': '消息', 10 | 'component.globalHeader.message.empty': '您已读完所有消息', 11 | 'component.globalHeader.event': '待办', 12 | 'component.globalHeader.event.empty': '你已完成所有待办', 13 | 'component.noticeIcon.clear': '清空', 14 | 'component.noticeIcon.cleared': '清空了', 15 | 'component.noticeIcon.empty': '暂无数据', 16 | 'component.noticeIcon.view-more': '查看更多', 17 | }; 18 | -------------------------------------------------------------------------------- /console/frontend/src/locales/zh-CN/pwa.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': '当前处于离线状态', 3 | 'app.pwa.serviceworker.updated': '有新内容', 4 | 'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面', 5 | 'app.pwa.serviceworker.updated.ok': '刷新', 6 | }; 7 | -------------------------------------------------------------------------------- /console/frontend/src/locales/zh-CN/settingDrawer.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.setting.pagestyle': '整体风格设置', 3 | 'app.setting.pagestyle.dark': '暗色菜单风格', 4 | 'app.setting.pagestyle.light': '亮色菜单风格', 5 | 'app.setting.content-width': '内容区域宽度', 6 | 'app.setting.content-width.fixed': '定宽', 7 | 'app.setting.content-width.fluid': '流式', 8 | 'app.setting.themecolor': '主题色', 9 | 'app.setting.themecolor.dust': '薄暮', 10 | 'app.setting.themecolor.volcano': '火山', 11 | 'app.setting.themecolor.sunset': '日暮', 12 | 'app.setting.themecolor.cyan': '明青', 13 | 'app.setting.themecolor.green': '极光绿', 14 | 'app.setting.themecolor.daybreak': '拂晓蓝(默认)', 15 | 'app.setting.themecolor.geekblue': '极客蓝', 16 | 'app.setting.themecolor.purple': '酱紫', 17 | 'app.setting.navigationmode': '导航模式', 18 | 'app.setting.sidemenu': '侧边菜单布局', 19 | 'app.setting.topmenu': '顶部菜单布局', 20 | 'app.setting.fixedheader': '固定 Header', 21 | 'app.setting.fixedsidebar': '固定侧边菜单', 22 | 'app.setting.fixedsidebar.hint': '侧边菜单布局时可配置', 23 | 'app.setting.hideheader': '下滑时隐藏 Header', 24 | 'app.setting.hideheader.hint': '固定 Header 时可配置', 25 | 'app.setting.othersettings': '其他设置', 26 | 'app.setting.weakmode': '色弱模式', 27 | 'app.setting.copy': '拷贝设置', 28 | 'app.setting.copyinfo': '拷贝成功,请到 config/defaultSettings.js 中替换默认配置', 29 | 'app.setting.production.hint': 30 | '配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件', 31 | }; 32 | -------------------------------------------------------------------------------- /console/frontend/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Morphling", 3 | "short_name": "Morphling", 4 | "display": "standalone", 5 | "start_url": "./?utm_source=homescreen", 6 | "theme_color": "#002140", 7 | "background_color": "#001529", 8 | "icons": [ 9 | { 10 | "src": "icons/icon-192x192.png", 11 | "sizes": "192x192" 12 | }, 13 | { 14 | "src": "icons/icon-128x128.png", 15 | "sizes": "128x128" 16 | }, 17 | { 18 | "src": "icons/icon-512x512.png", 19 | "sizes": "512x512" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /console/frontend/src/models/setting.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '../../config/defaultSettings'; 2 | 3 | const updateColorWeak = (colorWeak) => { 4 | const root = document.getElementById('root'); 5 | 6 | if (root) { 7 | root.className = colorWeak ? 'colorWeak' : ''; 8 | } 9 | }; 10 | 11 | const SettingModel = { 12 | namespace: 'settings', 13 | state: defaultSettings, 14 | reducers: { 15 | changeSetting(state = defaultSettings, {payload}) { 16 | const {colorWeak, contentWidth} = payload; 17 | 18 | if (state.contentWidth !== contentWidth && window.dispatchEvent) { 19 | window.dispatchEvent(new Event('resize')); 20 | } 21 | 22 | updateColorWeak(!!colorWeak); 23 | return {...state, ...payload}; 24 | }, 25 | }, 26 | }; 27 | export default SettingModel; 28 | -------------------------------------------------------------------------------- /console/frontend/src/models/user.js: -------------------------------------------------------------------------------- 1 | import {query as queryUsers, queryCurrent} from '@/services/user'; 2 | 3 | const UserModel = { 4 | namespace: 'user', 5 | state: { 6 | currentUser: {}, 7 | }, 8 | effects: { 9 | * fetch(_, {call, put}) { // 没有用到 10 | const response = yield call(queryUsers); 11 | yield put({ 12 | type: 'save', 13 | payload: response, 14 | }); 15 | }, 16 | 17 | * fetchCurrent(_, {call, put}) { // 在 BasicLayout 中用到 18 | const response = yield call(queryCurrent); 19 | yield put({ 20 | type: 'saveCurrentUser', // reducers 21 | payload: response, 22 | }); 23 | }, 24 | }, 25 | reducers: { 26 | saveCurrentUser(state, action) { 27 | return {...state, currentUser: action.payload || {}}; 28 | }, 29 | 30 | changeNotifyCount( 31 | state = { 32 | currentUser: {}, 33 | }, 34 | action, 35 | ) { 36 | return { 37 | ...state, 38 | currentUser: { 39 | ...state.currentUser, 40 | notifyCount: action.payload.totalCount, 41 | unreadCount: action.payload.unreadCount, 42 | }, 43 | }; 44 | }, 45 | }, 46 | }; 47 | export default UserModel; 48 | -------------------------------------------------------------------------------- /console/frontend/src/pages/404.jsx: -------------------------------------------------------------------------------- 1 | import {Button, Result} from 'antd'; 2 | import React from 'react'; 3 | import {history} from 'umi'; 4 | 5 | const NoFoundPage = () => ( 6 | history.push('/')}> 12 | Back Home 13 | 14 | } 15 | /> 16 | ); 17 | 18 | export default NoFoundPage; 19 | -------------------------------------------------------------------------------- /console/frontend/src/pages/Admin.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {HeartTwoTone, SmileTwoTone} from '@ant-design/icons'; 3 | import {Alert, Card, Typography} from 'antd'; 4 | import {PageHeaderWrapper} from '@ant-design/pro-layout'; 5 | import {useIntl} from 'umi'; 6 | 7 | export default () => { 8 | const intl = useIntl(); 9 | return ( 10 | 16 | 17 | 30 | 36 | Morphling You 37 | 38 | 39 |

45 | Want to add more pages? Please refer to{' '} 46 | 47 | use block 48 | 49 | 。 50 |

51 |
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /console/frontend/src/pages/ClusterInfo/service.js: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | 3 | const APIV1Prefix = "/api/v1alpha1"; 4 | 5 | export async function getOverviewTotal() { 6 | return request(APIV1Prefix + "/data/total"); 7 | } 8 | 9 | export async function getOverviewRequestPodPhase() { 10 | return request(APIV1Prefix + `/data/request/Running`); 11 | } 12 | 13 | export async function getOverviewNodeInfos(params) { 14 | const ret = await request(APIV1Prefix + "/data/nodeInfos", { 15 | params 16 | }); 17 | return ret; 18 | } 19 | -------------------------------------------------------------------------------- /console/frontend/src/pages/ClusterInfo/style.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .accounted { 4 | :global { 5 | .ant-progress-inner { 6 | background-color: white; 7 | } 8 | 9 | .ant-progress-inner { 10 | border-radius: 0; 11 | } 12 | 13 | .ant-progress-bg { 14 | border-radius: 0; 15 | } 16 | } 17 | 18 | .accountedNum { 19 | width: 35%; 20 | display: inline-block; 21 | } 22 | 23 | .accountedProgress { 24 | width: 65%; 25 | display: inline-block; 26 | } 27 | } 28 | 29 | .floatLeft { 30 | float: left; 31 | } 32 | 33 | .taskInfoTitle { 34 | float: left; 35 | margin-left: 10px; 36 | 37 | span { 38 | color: rgba(0, 0, 0, .45); 39 | font-size: 14px; 40 | line-height: 22px; 41 | } 42 | 43 | div { 44 | color: rgba(0, 0, 0, .85); 45 | font-size: 24px; 46 | white-space: nowrap; 47 | text-overflow: ellipsis; 48 | word-break: break-all; 49 | } 50 | } 51 | 52 | .taskInfoAvatarDiv { 53 | float: left; 54 | line-height: 50px; 55 | } 56 | 57 | .taskInfoAvatar { 58 | background-color: #1890ff; 59 | } 60 | 61 | .ackInfoIcon { 62 | background-color: #1890ff; 63 | margin-right: 10px; 64 | vertical-align: bottom; 65 | } 66 | -------------------------------------------------------------------------------- /console/frontend/src/pages/ExperimentCreate/components/FooterToolbar/index.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from "react"; 2 | import {RouteContext} from "@ant-design/pro-layout"; 3 | import classNames from "classnames"; 4 | import styles from "./index.less"; 5 | 6 | export default class FooterToolbar extends Component { 7 | getWidth = ({collapsed, isMobile, siderWidth}) => { 8 | const sider = document.querySelector(".ant-layout-sider"); 9 | 10 | if (!sider) { 11 | return undefined; 12 | } 13 | 14 | return isMobile 15 | ? undefined 16 | : `calc(100% - ${collapsed ? 80 : siderWidth || 256}px)`; 17 | }; 18 | 19 | render() { 20 | const {children, className, extra, ...restProps} = this.props; 21 | return ( 22 | 23 | {value => ( 24 |
32 |
{extra}
33 |
{children}
34 |
35 | )} 36 |
37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /console/frontend/src/pages/ExperimentCreate/components/FooterToolbar/index.less: -------------------------------------------------------------------------------- 1 | @import "~antd/es/style/themes/default.less"; 2 | 3 | .toolbar { 4 | position: fixed; 5 | right: 0; 6 | bottom: 0; 7 | z-index: 99; 8 | width: calc(100% - 150px) !important; 9 | height: 56px; 10 | padding: 0 24px; 11 | line-height: 56px; 12 | border-top: 1px solid @border-color-split; 13 | box-shadow: @box-shadow-base; 14 | background-color: white; 15 | 16 | &::after { 17 | display: block; 18 | clear: both; 19 | content: ""; 20 | } 21 | 22 | .left { 23 | float: left; 24 | } 25 | 26 | .right { 27 | float: right; 28 | } 29 | 30 | button + button { 31 | margin-left: 8px; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /console/frontend/src/pages/ExperimentCreate/components/SubmitModal.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Input, Modal} from "antd"; 3 | 4 | const TextArea = Input.TextArea; 5 | 6 | class SubmitModal extends React.Component { 7 | state = { 8 | loading: false, 9 | json: "" 10 | }; 11 | 12 | componentDidMount() { 13 | } 14 | 15 | onChange(e) { 16 | this.setState({ 17 | json: e.target.value 18 | }); 19 | } 20 | 21 | handleSubmit = async e => { 22 | const {onCancel} = this.props; 23 | try { 24 | this.setState({ 25 | loading: true 26 | }); 27 | await submitJob(this.state.json); 28 | onCancel(); 29 | } finally { 30 | this.setState({ 31 | loading: false 32 | }); 33 | } 34 | }; 35 | 36 | handleCancel = e => { 37 | const {onCancel} = this.props; 38 | onCancel(); 39 | }; 40 | 41 | render() { 42 | return ( 43 | 52 |