├── .dockerignore ├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .gitlab-ci.yml ├── LICENSE ├── Makefile ├── README.md ├── charts ├── cephfs-pvc │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── cephfs-pv.yaml │ │ ├── cephfs-pvc.yaml │ │ └── cephfs-secret.yaml │ └── values.yaml ├── environment │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── _helpers.tpl │ │ ├── environment-configmap.yaml │ │ ├── environment-info-configmap.yaml │ │ ├── environment-job.yaml │ │ ├── environment-jupyterlab-ingress.yaml │ │ ├── environment-jupyterlab-service.yaml │ │ ├── environment-secret.yaml │ │ └── environment-ssh-service.yaml │ └── values.yaml └── pineapple │ ├── Chart.yaml │ ├── templates │ ├── _helpers.tpl │ ├── billing │ │ ├── clusterrolebinding.yaml │ │ ├── depolyment.yaml │ │ ├── service.yaml │ │ └── serviceaccount.yaml │ ├── core │ │ ├── cephfs-pv.yaml │ │ ├── cephfs-pvc.yaml │ │ ├── cephfs-secret.yaml │ │ ├── clusterrolebinding.yaml │ │ ├── common-configmap.yaml │ │ ├── deployment.yaml │ │ ├── external-ingress.yaml │ │ ├── service.yaml │ │ └── serviceaccount.yaml │ ├── webhook │ │ ├── depolyment.yaml │ │ ├── job-patch │ │ │ ├── clusterrole.yaml │ │ │ ├── clusterrolebinding.yaml │ │ │ ├── job-createSecret.yaml │ │ │ ├── job-patchWebhook.yaml │ │ │ ├── psp.yaml │ │ │ ├── role.yaml │ │ │ ├── rolebinding.yaml │ │ │ └── serviceaccount.yaml │ │ ├── mutatingwebhookconfiguration.yaml │ │ ├── secret.yaml │ │ └── service.yaml │ └── webterminal │ │ ├── web-terminal-deployment.yaml │ │ ├── web-terminal-ingress.yaml │ │ └── web-terminal-service.yaml │ └── values.yaml ├── doc ├── api │ ├── billing.yaml │ ├── internal-api.yaml │ ├── main.yaml │ └── webterminal.yaml ├── img │ ├── quickstart1.png │ ├── quickstart2.png │ ├── quickstart3.png │ └── quickstart4.png └── tutorial │ └── custom-environment-image.md ├── dockerfile ├── Dockerfile ├── billing-server │ └── Dockerfile ├── webhook │ └── Dockerfile ├── webserver │ └── Dockerfile └── webterminal │ └── Dockerfile └── src ├── billing ├── README.md ├── conf │ └── conf.go ├── handler │ ├── account.go │ ├── computeunit.go │ └── handler.go ├── heartbeat.go ├── server.go └── utils │ ├── billing-utils.go │ ├── computeunit-utils.go │ └── k8s-utils.go ├── go.mod ├── go.sum ├── internal ├── auth │ ├── auth.go │ └── auth_test.go ├── billingclient │ ├── README.md │ └── billingclient.go ├── mongodb │ ├── README.md │ └── mongodb.go ├── response │ └── response.go └── version │ └── version.go ├── pineapple ├── conf │ └── conf.go ├── controller │ ├── application │ │ ├── app-info.go │ │ ├── app.go │ │ └── structs.go │ ├── environment │ │ ├── env-info.go │ │ ├── env.go │ │ └── structs.go │ └── pvc │ │ └── pvc.go ├── handler │ ├── application.go │ ├── appstore.go │ ├── competition.go │ ├── computing.go │ ├── environment.go │ ├── handler.go │ ├── images.go │ ├── models │ │ └── charts.go │ ├── others.go │ ├── release.go │ ├── storage.go │ ├── user.go │ └── version.go ├── init-user.go ├── server.go └── utils │ ├── harbor-utils.go │ ├── helm │ ├── chart.go │ ├── helm.go │ ├── pineapple-info.go │ ├── postrenderer.go │ └── resource.go │ ├── k8s-utils.go │ └── others.go ├── staticcheck.conf ├── webhook ├── README.md ├── admission_controller.go ├── generate-keys.sh └── server.go └── webterminal ├── gotty ├── .gotty ├── CONTRIBUTING.md ├── Makefile ├── README.md ├── backend │ ├── doc.go │ └── localcommand │ │ ├── doc.go │ │ ├── factory.go │ │ ├── local_command.go │ │ └── options.go ├── cache │ └── token │ │ ├── cache.go │ │ └── mem.go ├── favicon.psd ├── help.go ├── js │ ├── dist │ │ ├── gotty-bundle.js │ │ ├── hterm.d.ts │ │ ├── main.d.ts │ │ ├── websocket.d.ts │ │ ├── webtty.d.ts │ │ └── xterm.d.ts │ ├── src │ │ ├── hterm.ts │ │ ├── main.ts │ │ ├── websocket.ts │ │ ├── webtty.ts │ │ └── xterm.ts │ ├── tsconfig.json │ ├── typings │ │ └── libapps │ │ │ └── index.d.ts │ └── webpack.config.js ├── main.go ├── pkg │ ├── homedir │ │ └── expand.go │ └── randomstring │ │ └── generate.go ├── resources │ ├── favicon.png │ ├── index.css │ ├── index.html │ └── xterm_customize.css ├── screenshot.gif ├── server │ ├── asset.go │ ├── handler_atomic.go │ ├── handlers.go │ ├── init_message.go │ ├── list_address.go │ ├── log_response_writer.go │ ├── middleware.go │ ├── options.go │ ├── run_option.go │ ├── server.go │ ├── slave.go │ └── ws_wrapper.go ├── staticcheck.conf ├── utils │ ├── default.go │ └── flags.go ├── version.go ├── webtty │ ├── doc.go │ ├── errors.go │ ├── master.go │ ├── message_types.go │ ├── option.go │ ├── slave.go │ └── webtty.go └── wercker.yml └── server ├── controller └── webterminal │ └── webterminal.go ├── handler ├── handler.go └── terminal.go └── main.go /.dockerignore: -------------------------------------------------------------------------------- 1 | build 2 | bin 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | check: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: 1.16 21 | 22 | - run: make lint 23 | - run: make vet 24 | 25 | docker: 26 | runs-on: ubuntu-latest 27 | #services: 28 | # registry: 29 | # image: registry:2 30 | # ports: 31 | # - 5000:5000 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v2 35 | - name: Set up Docker Buildx 36 | id: buildx 37 | uses: docker/setup-buildx-action@v1 38 | - name: docker build 39 | run: > 40 | docker build -t openaios:${GITHUB_SHA} 41 | --build-arg VERSION=${VERSION} -f ./dockerfile/Dockerfile . 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | 10 | docker: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | - name: Get branch name 16 | uses: nelonoel/branch-name@v1.0.1 17 | - name: Set up Docker Buildx 18 | id: buildx 19 | uses: docker/setup-buildx-action@v1 20 | - name: Login to DockerHub 21 | uses: docker/login-action@v1 22 | with: 23 | username: ${{ secrets.DOCKERHUB_USERNAME }} 24 | password: ${{ secrets.DOCKERHUB_TOKEN }} 25 | - name: docker build and push 26 | run: | 27 | docker build -t 4pdosc/openaios:${BRANCH_NAME} \ 28 | --build-arg VERSION=${BRANCH_NAME} \ 29 | -f ./dockerfile/Dockerfile . 30 | docker push 4pdosc/openaios:${BRANCH_NAME} 31 | docker tag 4pdosc/openaios:${BRANCH_NAME} 4pdosc/openaios:latest 32 | docker push 4pdosc/openaios:latest 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .idea 3 | .DS_Store 4 | **/apigen 5 | build 6 | bin/ 7 | 8 | # dependencies 9 | /node_modules 10 | /npm-debug.log* 11 | /yarn-error.log 12 | 13 | # production 14 | /dist 15 | 16 | # misc 17 | .DS_Store 18 | 19 | # umi 20 | .umi 21 | .umi-production 22 | 23 | # portal 24 | /src/portal/node_modules/ 25 | /src/portal/dist/ 26 | .npmrc 27 | package.json 28 | package-lock.json 29 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - check 3 | - build_image 4 | - deploy 5 | 6 | variables: 7 | IMAGE_NAME: openaios 8 | 9 | static_check: 10 | stage: check 11 | image: '${BUILD_IMAGE}' 12 | script: 13 | - make lint 14 | - make vet 15 | 16 | .build_image: 17 | stage: build_image 18 | image: '${DIND_IMAGE}' 19 | script: 20 | - IMAGE_FULL_NAME=${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} 21 | - > 22 | docker build -t ${IMAGE_FULL_NAME} 23 | --build-arg BUILDBASE=${BUILD_IMAGE} 24 | --build-arg KUBECTLBASE=${KUBECTL_IMAGE} 25 | --build-arg TARGETBASE=${TARGET_IMAGE} 26 | --build-arg VERSION=${VERSION} 27 | --build-arg GOPROXY=${GOPROXY} -f ./dockerfile/Dockerfile . 28 | - docker push ${IMAGE_FULL_NAME} 29 | 30 | build_dev_image: 31 | extends: .build_image 32 | variables: 33 | IMAGE_TAG: ${CI_COMMIT_SHA} 34 | VERSION: ${CI_COMMIT_RREF_NAME}-${CI_COMMIT_SHA} 35 | only: 36 | - master 37 | 38 | build_release_image: 39 | extends: .build_image 40 | variables: 41 | IMAGE_TAG: ${CI_COMMIT_TAG} 42 | VERSION: ${CI_COMMIT_TAG}-${CI_COMMIT_SHA} 43 | only: 44 | - tags 45 | 46 | .deploy: 47 | stage: deploy 48 | image: '${HELM_IMAGE}' 49 | variables: 50 | RELEASE_NAME: pineapple 51 | RELEASE_NAMESPACE: pineapple 52 | EXTRA_ARGS: '' 53 | script: 54 | - IMAGE_REPO=${IMAGE_REGISTRY}/${IMAGE_NAME} 55 | - > 56 | helm upgrade --install ${RELEASE_NAME} ./charts/pineapple 57 | -n ${RELEASE_NAMESPACE} 58 | -f /secrets/openaios-values.yaml 59 | --set core.image.repository=${IMAGE_REPO} 60 | --set core.image.tag=${IMAGE_TAG} 61 | --set webterminal.image.repository=${IMAGE_REPO} 62 | --set webterminal.image.tag=${IMAGE_TAG} 63 | --set billing.image.repository=${IMAGE_REPO} 64 | --set billing.image.tag=${IMAGE_TAG} 65 | --set webhook.image.repository=${IMAGE_REPO} 66 | --set webhook.image.tag=${IMAGE_TAG} 67 | ${EXTRA_ARGS} 68 | 69 | deploy_develop: 70 | extends: .deploy 71 | variables: 72 | IMAGE_TAG: ${CI_COMMIT_SHA} 73 | environment: 74 | name: pineapple-develop 75 | only: 76 | - master 77 | tags: 78 | - deploy-test 79 | 80 | deploy_pre_product: 81 | extends: .deploy 82 | variables: 83 | IMAGE_TAG: ${CI_COMMIT_TAG} 84 | EXTRA_ARGS: "--wait --timeout=30m" 85 | environment: 86 | name: pineapple-develop 87 | only: 88 | - tags 89 | tags: 90 | - deploy-test 91 | 92 | deploy_product: 93 | extends: .deploy 94 | variables: 95 | IMAGE_TAG: ${CI_COMMIT_TAG} 96 | environment: 97 | name: pineapple-product 98 | only: 99 | - tags 100 | tags: 101 | - deploy-product 102 | when: manual 103 | 104 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO ?= go 2 | GO111MODULE = on 3 | GOPROXY ?= https://goproxy.cn,direct 4 | CGO_ENABLED = 0 5 | OUTPUT_DIR ?= bin 6 | VERSION ?= unknown 7 | BUILDARGS ?= -ldflags '-s -w -X github.com/4paradigm/openaios-platform/src/internal/version.version=$(VERSION)' 8 | 9 | all: pineapple billing webhook web-terminal gotty 10 | 11 | tidy: deps 12 | cd src && $(GO) mod tidy 13 | 14 | oapi_codegen: 15 | $(GO) get 'github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.5.5' 16 | 17 | apigen: oapi_codegen 18 | mkdir -p ./src/pineapple/apigen 19 | oapi-codegen -include-tags finished -generate types,server -package apigen ./doc/api/main.yaml > ./src/pineapple/apigen/serverapi.gen.go 20 | 21 | internalapigen: oapi_codegen 22 | mkdir -p ./src/pineapple/apigen/internalapigen 23 | oapi-codegen -include-tags finished -generate types,server -package internalapigen ./doc/api/internal-api.yaml > ./src/pineapple/apigen/internalapigen/internalapi.gen.go 24 | 25 | pineapple: apigen internalapigen billing_client_codegen tidy 26 | cd src && $(GO) build $(BUILDARGS) -o ../$(OUTPUT_DIR)/pineapple ./pineapple 27 | 28 | billing_oapi_codegen: oapi_codegen 29 | mkdir -p ./src/billing/apigen 30 | oapi-codegen -generate types,server -package apigen ./doc/api/billing.yaml > ./src/billing/apigen/billingapi.gen.go 31 | 32 | billing_client_codegen: oapi_codegen 33 | mkdir -p ./src/internal/billingclient/apigen 34 | oapi-codegen -generate types,client -package apigen ./doc/api/billing.yaml > ./src/internal/billingclient/apigen/billingclient.gen.go 35 | 36 | billing: billing_oapi_codegen billing_client_codegen tidy 37 | cd src && $(GO) build $(BUILDARGS) -o ../$(OUTPUT_DIR)/billing ./billing 38 | 39 | webhook: billing_client_codegen tidy 40 | cd src && $(GO) build $(BUILDARGS) -o ../$(OUTPUT_DIR)/webhook ./webhook 41 | 42 | webterminal_oapi_codegen: oapi_codegen 43 | mkdir -p ./src/webterminal/server/apigen 44 | oapi-codegen -include-tags finished -generate types,server -package apigen ./doc/api/webterminal.yaml > ./src/webterminal/server/apigen/serverapi.gen.go 45 | 46 | web-terminal: webterminal_oapi_codegen tidy 47 | cd src && $(GO) build $(BUILDARGS) -o ../$(OUTPUT_DIR)/web-terminal ./webterminal/server 48 | 49 | gotty: webterminal_oapi_codegen tidy 50 | cd src && $(GO) build $(BUILDARGS) -o ../$(OUTPUT_DIR)/gotty ./webterminal/gotty 51 | 52 | 53 | deps: apigen internalapigen billing_client_codegen billing_oapi_codegen webterminal_oapi_codegen 54 | 55 | lint: deps 56 | $(GO) install honnef.co/go/tools/cmd/staticcheck@latest 57 | cd src && staticcheck ./... 58 | 59 | vet: deps 60 | cd src && go vet ./... 61 | 62 | 63 | clean: 64 | rm -rf $(OUTPUT_DIR) src/pineapple/apigen src/billing/apigen src/internal/billingclient/apigen src/webterminal/server/apigen 65 | 66 | .PHONY: all clean 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAIOS-Platform 2 | 3 | 4 | [![build status](https://github.com/4paradigm/openaios-platform/actions/workflows/build.yml/badge.svg)](https://github.com/4paradigm/openaios-platform/actions/workflows/build.yml) 5 | [![docker pulls](https://img.shields.io/docker/pulls/4pdosc/openaios.svg)](https://hub.docker.com/r/4pdosc/openaios) 6 | 7 | ## 简介 8 | OpenAIOS Platform是一款基于OpenAIOS搭建起来的开发应用平台,OpenAIOS也依托于此对外输出核心能力,平台目前作为公有服务对外开放,网站地址是 https://openaios.4paradigm.com 。平台基于OpenAIOS搭建了一个统一的基础设施抽象层——算力规格,让应用开发者更少的关注基础设施;并且平台还提供AI的基础组件,让开发者在享受云原生带来的便利的同时,更加快速的开发、使用AI应用。 9 | 10 | 社区团队目前在进行积极的使用反馈收集和文档的迭代,在使用过程,遇到任何问题或疑问,都可以通过发送邮件到 opensource@4paradigm.com 来获取帮助。 11 | 12 | ## 部署 13 | 14 | ### 依赖 15 | 16 | - Kubernetes >= 1.16 17 | - NGINX Ingress Controller 18 | - Harbor 19 | - Keycloak 20 | - CephFS 21 | - Mongodb 22 | 23 | ### 安装 24 | 25 | 通过helm安装,配置好charts/pineapple/values.yaml,然后执行 26 | 27 | ``` 28 | helm install ${RELEASE_NAME} charts/pineapple -n ${RELEASE_NAMESPACE} 29 | ``` 30 | 31 | ## 基础概念解释 32 | ### 开发环境 33 | 开发环境是一个单pod容器,通过JupyterLab、SSH和WebTerminal与环境进行交互, JupyterLab和WebTerminal可以让我们基于网页与环境交互,SSH可以与本地IDE打通,例如vscode、pycharm等。 34 | 35 | ### 算力规格 36 | 每个算力规格可能包括CPU cores、内存、PMEM、GPU、FPGA等多种异构计算资源。可以根据自己所需的计算负载来选择相应的算力规格。不同的算力规格可以设置不同的计费规则用于管理。 37 | 38 | ### 镜像 39 | 镜像是运行时环境,在创建开发环境或者应用的时候选择,公共镜像包含主流的机器学习组件,可以满足大部分开发需求,私有镜像完全由用户自定义,当前我们支持从dockerhub引入镜像。 40 | 41 | 42 | ### 应用市场 43 | 应用市场目前采用了社区通用的Helm Chart通用部署规范,我们向应用开发者提供了一些轻量化允许用户定制的组件,支撑应用的快速开发和上线,降低开发门槛,最终开发者可对公网暴露出自己的一套服务;对于应用使用者则可以快速使用到第四范式自研的OpenMLDB等一系列应用。 44 | 45 | ### 快速上手 46 | #### 如何在5分钟实现线性回归预测过程 47 | 第一步,创建开发环境: 48 | - 选择所需的算力规格 49 | - 选择镜像 50 | - 挂载数据,文件管理中的训练数据和代码可以挂载进开发环境内 51 | - 选择交互方式,选用JupyterLab进行交互,需要设置一个token 52 | 最后,点击“准备环境”。 53 | 54 | 55 | ![1](doc/img/quickstart1.png) 56 | ![2](doc/img/quickstart2.png) 57 | 58 | 59 | 第二步,通过Jupyterlab访问开发环境,点击下方的图标即可进入。 60 | ![3](doc/img/quickstart3.png) 61 | 62 | 第三步,来到了Jupyterlab,Jupyterlab左侧是挂载进去的数据和代码,可以选择自带的python和终端等来编辑和运行代码。 63 | ![4](doc/img/quickstart4.png) 64 | 65 | 66 | -------------------------------------------------------------------------------- /charts/cephfs-pvc/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/cephfs-pvc/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: cephfs-pvc 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: 1.16.0 24 | -------------------------------------------------------------------------------- /charts/cephfs-pvc/templates/cephfs-pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: pineapple-{{ .Values.userId }}-pv 5 | labels: 6 | app: cephfs 7 | role: pv 8 | name: {{ .Release.Name }} 9 | user: {{ .Values.userId }} 10 | spec: 11 | storageClassName: "ceph-storage" 12 | claimRef: 13 | name: remote-storage 14 | namespace: {{ .Values.userId }} 15 | capacity: 16 | storage: {{ .Values.capacity.storage }} 17 | accessModes: 18 | - ReadWriteMany 19 | cephfs: 20 | {{- toYaml .Values.cephfs | nindent 4 }} 21 | secretRef: 22 | name: pineapple-ceph-secret 23 | -------------------------------------------------------------------------------- /charts/cephfs-pvc/templates/cephfs-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: remote-storage 5 | labels: 6 | app: cephfs 7 | role: pvc 8 | name: {{ .Release.Name }} 9 | spec: 10 | accessModes: 11 | - ReadWriteMany 12 | storageClassName: "ceph-storage" 13 | resources: 14 | requests: 15 | storage: "{{ .Values.capacity.storage }}" 16 | selector: 17 | matchLabels: 18 | app: cephfs 19 | -------------------------------------------------------------------------------- /charts/cephfs-pvc/templates/cephfs-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: pineapple-ceph-secret 5 | labels: 6 | app: cephfs 7 | role: secret 8 | name: {{ .Release.Name }} 9 | data: 10 | key: {{ .Values.cephSecret.key }} 11 | -------------------------------------------------------------------------------- /charts/cephfs-pvc/values.yaml: -------------------------------------------------------------------------------- 1 | userId: "" 2 | 3 | cephfs: 4 | monitors: [] 5 | path: "" 6 | user: "" 7 | 8 | capacity: 9 | storage: "" 10 | 11 | cephSecret: 12 | key: "" 13 | -------------------------------------------------------------------------------- /charts/environment/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/environment/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: environment 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: 1.16.0 24 | -------------------------------------------------------------------------------- /charts/environment/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "environment.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "environment.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "environment.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "environment.labels" -}} 37 | helm.sh/chart: {{ include "environment.chart" . }} 38 | {{ include "environment.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "environment.selectorLabels" -}} 49 | appName: {{ .Chart.Name }} 50 | name: {{ .Release.Name }} 51 | app.kubernetes.io/name: {{ include "environment.name" . }} 52 | {{- end }} -------------------------------------------------------------------------------- /charts/environment/templates/environment-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "environment.fullname" . }} 5 | labels: 6 | {{- include "environment.labels" . | nindent 4 }} 7 | data: 8 | entrypoint.sh: | 9 | if [ ${USE_SSH} = "True" ]; then 10 | mkdir /run/sshd 11 | if [ ! -f /etc/ssh/ssh_host_rsa_key ] || [ ! -f /etc/ssh/ssh_host_ecdsa_key ] || [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then 12 | ssh-keygen -A 13 | fi 14 | CAT_PATH=$(which cat) 15 | /usr/sbin/sshd \ 16 | -o "AuthorizedKeysCommand ${CAT_PATH} /pineapple/secret/ssh/ssh-key" \ 17 | -o "AuthorizedKeysCommandUser root" 18 | if [ $? -ne 0 ]; then 19 | echo "ERROR: ssh start failed!" 20 | exit 1 21 | fi 22 | fi 23 | if [ ${USE_JUPYTER} = "True" ]; then 24 | exec jupyter-lab --debug --no-browser \ 25 | --ip="0.0.0.0" \ 26 | --port=8888 \ 27 | --allow-root \ 28 | {{- if .Values.jupyter.token }} 29 | --LabApp.token={{ .Values.jupyter.token | quote }} \ 30 | {{- else }} 31 | --LabApp.token='' \ 32 | {{- end }} 33 | --LabApp.password='' \ 34 | --LabApp.base_url="/ingress/{{ .Release.Namespace }}/env/{{ .Release.Name }}/jupyterlab" 35 | elif [ ${USE_JUPYTER} = "False" ]; then 36 | exec tail -f /dev/null 37 | fi -------------------------------------------------------------------------------- /charts/environment/templates/environment-info-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "environment.fullname" . }}-info 5 | labels: 6 | {{- include "environment.labels" . | nindent 4 }} 7 | data: 8 | releaseInfo: | 9 | PINEAPPLE_ENV_INFO_START_HERE<<< 10 | { 11 | "name": "{{ .Release.Name }}", 12 | "create_tm": "", 13 | "environmentConfig": { 14 | "image": { 15 | "repository": "{{ .Values.image.repository }}", 16 | "tag": "{{ .Values.image.tag }}" 17 | }, 18 | "mounts": {{ toJson .Values.volumeMounts }}, 19 | "compute_unit": "{{ .Values.pineapple.default.resourceId }}", 20 | "ssh": { 21 | "enable": {{ .Values.serverType.ssh | lower }}, 22 | "id_rsa.pub": {{ .Values.ssh.sshKey | quote }} 23 | }, 24 | "jupyter": { 25 | "enable": {{ .Values.serverType.jupyter | lower }}, 26 | "token": {{ .Values.jupyter.token | quote }} 27 | } 28 | }, 29 | "notebook_url": "/ingress/{{ .Release.Namespace }}/env/{{ .Release.Name }}/jupyterlab" 30 | } 31 | << 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 conf implements methods to get mongodb configs from flags or environment variables. 18 | package conf 19 | 20 | import ( 21 | "flag" 22 | "os" 23 | ) 24 | 25 | var ( 26 | mongodbURL = flag.String("mongodb-url", os.Getenv("PINEAPPLE_MONGODB_URL"), 27 | "mongodb url") 28 | mongodbDatabase = flag.String("mongodb-database", os.Getenv("PINEAPPLE_MONGODB_DATABASE"), 29 | "mongodb database") 30 | ) 31 | 32 | // GetmongodbURL returns mongodbURL string 33 | func GetmongodbURL() string { 34 | return *mongodbURL 35 | } 36 | 37 | // GetMongodbDatabase returns mongodbDatabase string 38 | func GetMongodbDatabase() string { 39 | return *mongodbDatabase 40 | } 41 | -------------------------------------------------------------------------------- /src/billing/handler/handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler implements methods for 18 | package handler 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/billing/conf" 22 | "github.com/4paradigm/openaios-platform/src/billing/utils" 23 | "github.com/4paradigm/openaios-platform/src/internal/mongodb" 24 | "github.com/labstack/gommon/log" 25 | ) 26 | 27 | type Handler struct{} 28 | 29 | var mongodbURL = conf.GetmongodbURL() 30 | 31 | func NewHandler() Handler { 32 | return Handler{} 33 | } 34 | 35 | func InitBillingServer() { 36 | // init mongodb collection 37 | client, err := mongodb.GetMongodbClient(mongodbURL) 38 | defer mongodb.KillMongodbClient(client) 39 | if err != nil { 40 | log.Error(err.Error()) 41 | return 42 | } 43 | if err = utils.InitColl(client); err != nil { 44 | log.Error(err.Error()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/billing/heartbeat.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 main 18 | 19 | import ( 20 | "encoding/json" 21 | "github.com/4paradigm/openaios-platform/src/billing/conf" 22 | "github.com/4paradigm/openaios-platform/src/billing/utils" 23 | "github.com/4paradigm/openaios-platform/src/internal/mongodb" 24 | "github.com/4paradigm/openaios-platform/src/internal/response" 25 | "github.com/labstack/gommon/log" 26 | "github.com/pkg/errors" 27 | "github.com/robfig/cron/v3" 28 | "time" 29 | ) 30 | 31 | func run() { 32 | mongodbURL := conf.GetmongodbURL() 33 | 34 | // init mongodb client 35 | client, err := mongodb.GetMongodbClient(mongodbURL) 36 | defer mongodb.KillMongodbClient(client) 37 | if err != nil { 38 | log.Error(err.Error()) 39 | return 40 | } 41 | 42 | // init k8s client 43 | kubeClient, err := utils.GetKubernetesClient() 44 | if err != nil { 45 | log.Error(err.Error()) 46 | } 47 | 48 | // get user pod list in k8s 49 | podList, err := utils.GetPodList(kubeClient, "openaios.4paradigm.com/app=true", "") 50 | if err != nil { 51 | log.Error(err.Error()) 52 | return 53 | } 54 | 55 | // init user bill map and price list 56 | billMap := map[string]float64{} 57 | priceMap, err := utils.GetPriceMap(client) 58 | if err != nil { 59 | log.Error(err.Error()) 60 | return 61 | } 62 | 63 | // deal with user account for each pod 64 | for _, pod := range *podList { 65 | // read pod information 66 | userID := pod.Namespace 67 | podName := pod.Name 68 | podUID := pod.UID 69 | instanceID := pod.Labels["instanceID"] 70 | updateTime := time.Now() 71 | startTime := updateTime 72 | if pod.Status.StartTime != nil { 73 | startTime = pod.Status.StartTime.Time 74 | } 75 | status := string(pod.Status.Phase) 76 | 77 | // only care running pod 78 | if status != "Running" || pod.DeletionTimestamp != nil { 79 | continue 80 | } 81 | 82 | var computeunitList []string 83 | computeunitString := pod.Annotations["openaios.4paradigm.com/computeunitList"] 84 | if computeunitString == "" { 85 | log.Warnf("user %s's pod %s does not have computeunit list", userID, podName) 86 | continue 87 | } 88 | err = json.Unmarshal([]byte(computeunitString), &computeunitList) 89 | if err != nil { 90 | log.Error(err) 91 | continue 92 | } 93 | for _, computeunit := range computeunitList { 94 | computeunitPrice, ok := priceMap[computeunit] 95 | if !ok { 96 | log.Warn("price map does not have such compute price " + computeunit) 97 | } 98 | billMap[userID] -= computeunitPrice 99 | } 100 | 101 | // update mongodb 102 | err = utils.UpdateOrInsertPod(client, utils.PodInfo{UserID: userID, PodName: podName, PodUID: podUID, 103 | InstanceID: instanceID, ComputeunitList: computeunitList, StartTime: startTime, UpdateTime: updateTime}) 104 | if err != nil { 105 | log.Error(err) 106 | } 107 | } 108 | 109 | // check user account map 110 | for userID, cost := range billMap { 111 | err = utils.ModifyUserBalance(client, userID, cost) 112 | if err != nil { 113 | log.Error(errors.Wrap(err, response.GetRuntimeLocation())) 114 | } 115 | } 116 | 117 | // check user no balance 118 | err = utils.CheckUserNoBalance(client, billMap) 119 | if err != nil { 120 | log.Error(errors.Wrap(err, response.GetRuntimeLocation())) 121 | } 122 | } 123 | 124 | func heartbeat() { 125 | c := cron.New() 126 | _, err := c.AddFunc("* * * * *", run) 127 | //_, err := c.AddFunc("@every 1s", run) 128 | if err != nil { 129 | log.Error(errors.Wrap(err, response.GetRuntimeLocation())) 130 | return 131 | } 132 | c.Start() 133 | select {} 134 | } 135 | -------------------------------------------------------------------------------- /src/billing/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | "github.com/4paradigm/openaios-platform/src/billing/apigen" 23 | "github.com/4paradigm/openaios-platform/src/billing/handler" 24 | "github.com/4paradigm/openaios-platform/src/internal/response" 25 | "github.com/labstack/echo/v4" 26 | "github.com/labstack/echo/v4/middleware" 27 | "github.com/labstack/gommon/log" 28 | ) 29 | 30 | func main() { 31 | flag.Parse() 32 | fmt.Printf("FLAGS:\n") 33 | flag.VisitAll(func(f *flag.Flag) { 34 | fmt.Printf("%-25v : %v\n", f.Name, f.Value.String()) 35 | }) 36 | 37 | e := echo.New() 38 | e.Logger.SetHeader(`${time_rfc3339} ${level} ${short_file}:${line} `) 39 | e.Logger.SetLevel(log.WARN) 40 | e.HTTPErrorHandler = response.CustomHTTPErrorHandler 41 | 42 | e.Use(middleware.Logger()) 43 | h := handler.NewHandler() 44 | handler.InitBillingServer() 45 | 46 | apiGroup := e.Group("/api") 47 | apigen.RegisterHandlers(apiGroup, &h) 48 | go heartbeat() 49 | e.Logger.Info(e.Start("0.0.0.0:80")) 50 | } 51 | -------------------------------------------------------------------------------- /src/billing/utils/k8s-utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 utils 18 | 19 | import ( 20 | "context" 21 | "github.com/pkg/errors" 22 | v1 "k8s.io/api/core/v1" 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/client-go/kubernetes" 25 | "k8s.io/client-go/rest" 26 | "k8s.io/client-go/tools/clientcmd" 27 | "k8s.io/client-go/util/homedir" 28 | "path/filepath" 29 | ) 30 | 31 | func GetKubernetesClient() (*kubernetes.Clientset, error) { 32 | var config *rest.Config 33 | var err error 34 | 35 | config, err = rest.InClusterConfig() 36 | if err == rest.ErrNotInCluster { 37 | var kubeconfig string 38 | if home := homedir.HomeDir(); home != "" { 39 | kubeconfig = filepath.Join(home, ".kube", "config") 40 | } else { 41 | return nil, errors.New("HOME env not found") 42 | } 43 | 44 | // use the current context in kubeconfig 45 | config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) 46 | if err != nil { 47 | return nil, err 48 | } 49 | } else if err != nil { 50 | return nil, errors.WithMessagef(err, "trying to load in cluster kubernetes config, but failed") 51 | } 52 | 53 | client, err := kubernetes.NewForConfig(config) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return client, nil 58 | } 59 | 60 | func GetPodList(client *kubernetes.Clientset, labelSelector string, ns string) (*[]v1.Pod, error) { 61 | pods, err := client.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{ 62 | LabelSelector: labelSelector, 63 | }) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return &pods.Items, nil 68 | } -------------------------------------------------------------------------------- /src/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/4paradigm/openaios-platform/src 2 | 3 | go 1.14 4 | 5 | require ( 6 | code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48 7 | github.com/AimAlex/harbor-client v0.2.0 8 | github.com/NYTimes/gziphandler v1.1.1 9 | github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect 10 | github.com/coreos/go-oidc/v3 v3.0.0 11 | github.com/creack/pty v1.1.11 12 | github.com/deepmap/oapi-codegen v1.5.5 13 | github.com/elazarl/go-bindata-assetfs v1.0.1 14 | github.com/fatih/structs v1.1.0 15 | github.com/go-openapi/runtime v0.19.5 // do not use go-openapi >= v0.20.0 16 | github.com/go-openapi/strfmt v0.19.5 17 | github.com/gorilla/websocket v1.4.2 18 | github.com/klauspost/compress v1.9.5 19 | github.com/labstack/echo-contrib v0.9.0 20 | github.com/labstack/echo/v4 v4.2.1 21 | github.com/labstack/gommon v0.3.0 22 | github.com/mailru/easyjson v0.7.7 // indirect 23 | github.com/patrickmn/go-cache v2.1.0+incompatible 24 | github.com/pkg/errors v0.9.1 25 | github.com/robfig/cron/v3 v3.0.1 26 | github.com/urfave/cli/v2 v2.3.0 27 | go.mongodb.org/mongo-driver v1.5.0 28 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect 29 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 30 | helm.sh/helm/v3 v3.6.1 31 | k8s.io/api v0.21.2 32 | k8s.io/apimachinery v0.21.2 33 | k8s.io/cli-runtime v0.21.2 34 | k8s.io/client-go v0.21.2 35 | sigs.k8s.io/kustomize/api v0.8.10 36 | ) 37 | 38 | replace ( 39 | github.com/docker/distribution => github.com/docker/distribution v0.0.0-20191216044856-a8371794149d 40 | github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible 41 | ) 42 | -------------------------------------------------------------------------------- /src/internal/auth/auth.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 auth implements auth methods 18 | package auth 19 | 20 | import ( 21 | "context" 22 | "flag" 23 | oidc "github.com/coreos/go-oidc/v3/oidc" 24 | "github.com/pkg/errors" 25 | "os" 26 | ) 27 | 28 | var ( 29 | keycloakURL = flag.String("keycloak-url", os.Getenv("PINEAPPLE_OIDC_KEYCLOAK_URL"), 30 | "oidc keycloak url like https://keycloak.pineapple.com:32443/auth/realms/develop") 31 | clientID = flag.String("oidc-client-id", os.Getenv("PINEAPPLE_OIDC_CLIENT_ID"), 32 | "oidc client id") 33 | verifier *oidc.IDTokenVerifier = nil 34 | ) 35 | 36 | type IDTokenClaim struct { 37 | Iss string 38 | Sub string 39 | PreferredUserName string `json:"preferred_username"` 40 | Email string 41 | } 42 | 43 | func InitAuth() error { 44 | if *keycloakURL == "" { 45 | return errors.New("flag to variable keycloakURL is not set") 46 | } 47 | if *clientID == "" { 48 | return errors.New("flag to variable clientID is not set") 49 | } 50 | 51 | provider, err := oidc.NewProvider(context.TODO(), *keycloakURL) 52 | if err != nil { 53 | return errors.Wrap(err, "failed to new provider") 54 | } 55 | 56 | oidcConfig := &oidc.Config{ 57 | ClientID: *clientID, 58 | } 59 | verifier = provider.Verifier(oidcConfig) 60 | return nil 61 | } 62 | 63 | func Verify(key string) (*IDTokenClaim, error) { 64 | if verifier == nil { 65 | return nil, errors.New("verifier not initialized") 66 | } 67 | idToken, err := verifier.Verify(context.TODO(), key) 68 | if err != nil { 69 | return nil, errors.Wrap(err, "token verify failed") 70 | } 71 | 72 | var idTokenClaim IDTokenClaim 73 | if err := idToken.Claims(&idTokenClaim); err != nil { 74 | return nil, errors.Wrap(err, "IDToken claim cast failed") 75 | } 76 | return &idTokenClaim, nil 77 | } 78 | -------------------------------------------------------------------------------- /src/internal/auth/auth_test.go: -------------------------------------------------------------------------------- 1 | package auth 2 | -------------------------------------------------------------------------------- /src/internal/billingclient/README.md: -------------------------------------------------------------------------------- 1 | # Billing Server Client 2 | 3 | 我们通过openapi生成了billing server client端的代码,但为了更方便的使用,我们在它的基础上包装了一层,主要函数的功能如下: 4 | 5 | - GetBillingClient:创建billing server的client 6 | - InitUserBillingAccount:初始化用户的账户,目前初始金额是hardcode到代码里的,之后可以移到数据库 7 | - GetUserBalance:获取用户的余额 8 | - GetOneComputeUnit:获取一个算力规格的具体内容 9 | - GetComputeUnitListByUserID:获取一个用户可以使用的所有算力规格 10 | - GetComputeUnitListByGroupName:获取指定group下所有的算力规格 11 | - GetComputeUnitPrice:获取指定算力规格的价格 12 | - AddComputeunitGroupToUser:给用户添加算力规格的group 13 | 14 | 目前这个工具包会被webserver以及webhook使用。 -------------------------------------------------------------------------------- /src/internal/mongodb/README.md: -------------------------------------------------------------------------------- 1 | # MongoDB 工具包 2 | 3 | 因为之前比较好用的mgo库已经无人维护,所以我们使用了mongodb的官方sdk:MongoDB Go Driver(https://github.com/mongodb/mongo-go-driver) 4 | 但是因为这个SDK中的函数都较为底层不方便使用,我们自己在它的基础上又封装了一层。 5 | 6 | 当然,目前来看这个工具包只实现了一些简单的功能,如果有额外需求可以自行添加,下面简单介绍一下每个函数 7 | 8 | - GetMongodbClient:创建一个mongodb的client 9 | - KillMongodbClient:回收一个mongodb的client 10 | - CreateIndex:为collection创建一个index(非unique、目前只支持单个key) 11 | - CreateUniqueIndex:为collection创建一个 unique index(可以是多个key组成的对) 12 | - CountDocuments:在collection下对制定条件的document计数 13 | - InsertOneDocument:插入一个document 14 | - DeleteOneDocument:删除一个document 15 | - UpdateOneDocument:更新一个document,支持多个operator,但是每个operator只能出现一次 16 | - FindOneDocuemnt:找到一个document 17 | - FindDoucments:找到满足条件的多个document,目前只支持对单个key的比较 18 | - FindDocumentsByMultiKey:通过多个key找到满足条件的多个documents,目前只支持等于 19 | - UpdateOrInsertOneDocument:更新或者插入一个document,支持多个operator,但是每个operator只能出现一次 20 | 21 | 我们MongoDB的存储格式可以参考doc/developer的下的database-structure.md文件。 -------------------------------------------------------------------------------- /src/internal/response/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 response provides webserver's response. 18 | package response 19 | 20 | import ( 21 | "flag" 22 | "fmt" 23 | "github.com/labstack/echo/v4" 24 | "net/http" 25 | "runtime" 26 | ) 27 | 28 | var debug = flag.Bool("debug", false, "enable debug mode") 29 | 30 | func BadRequestWithMessage(c echo.Context, message string) error { 31 | return c.JSON(http.StatusBadRequest, map[string]string{ 32 | "message": message, 33 | }) 34 | } 35 | 36 | func BadRequestWithMessagef(c echo.Context, format string, args ...interface{}) error { 37 | return c.JSON(http.StatusBadRequest, map[string]string{ 38 | "message": fmt.Sprintf(format, args...), 39 | }) 40 | } 41 | 42 | func BadRequestWithMessageWithJSON(c echo.Context, message string, content interface{}) error { 43 | return c.JSON(http.StatusBadRequest, map[string]interface{}{ 44 | "message": message, 45 | "type": "json", 46 | "content": content, 47 | }) 48 | } 49 | 50 | func StatusOKNoContent(c echo.Context) error { 51 | return c.String(http.StatusOK, "success") 52 | } 53 | 54 | func CustomHTTPErrorHandler(err error, c echo.Context) { 55 | he, ok := err.(*echo.HTTPError) 56 | if !ok { 57 | he = &echo.HTTPError{ 58 | Code: http.StatusInternalServerError, 59 | Message: http.StatusText(http.StatusInternalServerError), 60 | } 61 | } 62 | 63 | // Issue #1426 64 | code := he.Code 65 | message := he.Message 66 | if Debug() { 67 | message = err.Error() 68 | } else if m, ok := message.(string); ok { 69 | message = map[string]string{"message": m} 70 | } 71 | 72 | // Send response 73 | if !c.Response().Committed { 74 | if code == http.StatusInternalServerError { 75 | if he.Internal != nil { 76 | c.Logger().Error(he.Internal.Error()) 77 | } else { 78 | c.Logger().Error("http error without internal: " + err.Error()) 79 | } 80 | } 81 | 82 | if c.Request().Method == http.MethodHead { // Issue #608 83 | err = c.NoContent(he.Code) 84 | } else { 85 | err = c.JSON(code, message) 86 | } 87 | if err != nil { 88 | c.Logger().Error(err) 89 | } 90 | } 91 | } 92 | 93 | func Debug() bool { 94 | return *debug 95 | } 96 | 97 | func GetRuntimeLocation() string { 98 | _, fn, line, _ := runtime.Caller(1) 99 | return fmt.Sprintf("%s:%d", fn, line) 100 | } 101 | -------------------------------------------------------------------------------- /src/internal/version/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 version provides version functions 18 | package version 19 | 20 | import ( 21 | "flag" 22 | "fmt" 23 | "os" 24 | ) 25 | 26 | var ( 27 | version string 28 | printVersion = flag.Bool("version", false, "print version") 29 | ) 30 | 31 | func GetVersion() string { 32 | return version 33 | } 34 | 35 | // CheckVersionFlag if version flag == true, then print version info and exit 36 | func CheckVersionFlag() { 37 | if *printVersion { 38 | fmt.Printf("version: %v\n", GetVersion()) 39 | os.Exit(0) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/pineapple/controller/application/app-info.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 application provides controller for application. 18 | package application 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/pineapple/utils/helm" 22 | ) 23 | 24 | type ApplicationInfo struct { 25 | *helm.PineappleInfo 26 | } 27 | -------------------------------------------------------------------------------- /src/pineapple/controller/application/structs.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 application provides controller for application. 18 | package application 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 22 | "github.com/pkg/errors" 23 | v1 "k8s.io/api/core/v1" 24 | "time" 25 | ) 26 | 27 | const ( 28 | AppPrefix = "app-" 29 | ) 30 | 31 | type ApplicationInstanceInfo struct { 32 | AppName string `json:"instance_name,omitempty"` 33 | ChartName string `json:"chart_name,omitempty"` 34 | ChartVersion string `json:"chart_version,omitempty"` 35 | CreateTm time.Time `json:"create_tm,omitempty"` 36 | Duration time.Duration `json:"-"` 37 | Status string `json:"status,omitempty"` 38 | } 39 | 40 | type ApplicationInstanceInfos struct { 41 | Item *[]ApplicationInstanceInfo `json:"item,omitempty"` 42 | Total *int `json:"total,omitempty"` 43 | } 44 | 45 | type ApplicationReleaseInfo struct { 46 | ReleaseName string `json:"-"` 47 | Type string `json:"-"` 48 | } 49 | 50 | type ApplicationPodInfo struct { 51 | *ApplicationReleaseInfo 52 | Total int `json:"total,omitempty"` 53 | Pods []PodInfo `json:"item,omitempty"` 54 | } 55 | 56 | type PodInfo struct { 57 | Name string `json:"name,omitempty"` 58 | Status v1.PodPhase `json:"state,omitempty"` 59 | Containers []ContainerInfo `json:"containers,omitempty"` 60 | Event []EventInfo `json:"events,omitempty"` 61 | CreateTm time.Time `json:"create_tm,omitempty"` 62 | } 63 | 64 | type ContainerInfo struct { 65 | Name string `json:"name,omitempty"` 66 | Image string `json:"image,omitempty"` 67 | State string `json:"state,omitempty"` 68 | Ports []ContainerPort `json:"ports,omitempty"` 69 | } 70 | 71 | type ContainerPort struct { 72 | ContainerPort string `json:"container_port,omitempty"` 73 | Protocol string `json:"protocol,omitempty"` 74 | } 75 | 76 | type EventInfo struct { 77 | Age string `json:"age,omitempty"` 78 | From string `json:"from,omitempty"` 79 | Message string `json:"message,omitempty"` 80 | Reason string `json:"reason,omitempty"` 81 | Type string `json:"type,omitempty"` 82 | } 83 | 84 | type ApplicationServiceInfo struct { 85 | *ApplicationReleaseInfo 86 | Total int `json:"total,omitempty"` 87 | Services []ServiceInfo `json:"item,omitempty"` 88 | } 89 | 90 | type ServiceInfo struct { 91 | Name string `json:"name,omitempty"` 92 | ClusterIP string `json:"cluster_ip,omitempty"` 93 | ExternalIPs []string `json:"external_ips,omitempty"` 94 | Type v1.ServiceType `json:"type,omitempty"` 95 | Ports []ServicePort `json:"ports,omitempty"` 96 | } 97 | 98 | type ServicePort struct { 99 | Name string `json:"name,omitempty"` 100 | Port string `json:"port,omitempty"` 101 | NodePort string `json:"node_port,omitempty"` 102 | Protocol string `json:"protocol,omitempty"` 103 | } 104 | 105 | type ApplicationNotes struct { 106 | Notes string `json:"notes,omitempty"` 107 | } 108 | 109 | func (a *ApplicationReleaseInfo) GetReleaseName() (string, error) { 110 | if a.ReleaseName == "" { 111 | return "", errors.New("Release Name cannot be found in ApplicationReleaseInfo: " + utils.GetRuntimeLocation()) 112 | } 113 | return a.ReleaseName, nil 114 | } 115 | 116 | func (a *ApplicationReleaseInfo) GetType() (string, error) { 117 | if a.Type == "" { 118 | return "", errors.New("Release Type cannot be found in ApplicationReleaseInfo: " + utils.GetRuntimeLocation()) 119 | } 120 | return a.Type, nil 121 | } 122 | -------------------------------------------------------------------------------- /src/pineapple/controller/environment/env-info.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 environment provides controller for environment. 18 | package environment 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/pineapple/conf" 22 | "github.com/4paradigm/openaios-platform/src/pineapple/handler/models" 23 | "github.com/4paradigm/openaios-platform/src/pineapple/utils/helm" 24 | "github.com/fatih/structs" 25 | ) 26 | 27 | type EnvironmentInfo struct { 28 | *helm.PineappleInfo 29 | Image models.ImageInfo 30 | ServerType ServerType 31 | SSHKey string 32 | JupyterToken string 33 | PvcClaimName string 34 | VolumeMounts models.VolumeMounts 35 | ResourceID string 36 | } 37 | 38 | func (e *EnvironmentInfo) CreateEnvValues() (map[string]interface{}, error) { 39 | envValues := make(map[string]interface{}) 40 | envValues["image"] = structs.Map(e.Image) 41 | 42 | envValues["serverType"] = structs.Map(e.ServerType) 43 | envValues["ssh"] = map[string]string{ 44 | "sshKey": e.SSHKey, 45 | } 46 | envValues["jupyter"] = map[string]string{ 47 | "token": e.JupyterToken, 48 | } 49 | envValues["pvc"] = map[string]string{ 50 | "claimName": e.PvcClaimName, 51 | } 52 | envValues["volumeMounts"] = e.VolumeMounts 53 | envValues["ingress"] = map[string]interface{}{ 54 | "host": conf.GetExternalURLHost(), 55 | "enableTLS": conf.GetExternalTLS(), 56 | } 57 | 58 | envValues["pineapple"] = map[string]interface{}{ 59 | "belongTo": "user", 60 | "default": map[string]interface{}{ 61 | "resourceId": e.ResourceID, 62 | }, 63 | } 64 | 65 | return envValues, nil 66 | } 67 | -------------------------------------------------------------------------------- /src/pineapple/controller/pvc/pvc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 pvc provides controller for pvc. 18 | package pvc 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 22 | "github.com/4paradigm/openaios-platform/src/pineapple/utils/helm" 23 | "github.com/fatih/structs" 24 | "github.com/pkg/errors" 25 | "helm.sh/helm/v3/pkg/action" 26 | "helm.sh/helm/v3/pkg/chart/loader" 27 | "helm.sh/helm/v3/pkg/release" 28 | ) 29 | 30 | type SecretRef struct { 31 | Name string `json:"name" structs:"name"` 32 | } 33 | 34 | //type CephfsInfo struct { 35 | // Monitors []string `json:"monitors" structs:"monitors"` 36 | // Path string `json:"path" structs:"path"` 37 | // User string `json:"user" structs:"user"` 38 | // SecretRef *SecretRef `json:"secretRef" structs:"secretRef"` 39 | //} 40 | 41 | type Capacity struct { 42 | Storage string `structs:"storage"` 43 | } 44 | 45 | type CephSecret struct { 46 | Key string `structs:"key"` 47 | } 48 | 49 | type PvcInfo struct { 50 | UserID string `structs:"userId"` 51 | Cephfs *map[string]interface{} `structs:"cephfs"` 52 | Capacity *Capacity `structs:"capacity"` 53 | CephSecret *CephSecret `structs:"cephSecret"` 54 | } 55 | 56 | type PvcImpl struct { 57 | *helm.HelmClientImpl 58 | } 59 | 60 | func NewPvcImpl(kubeToken string, namespace string) (*PvcImpl, error) { 61 | helmClientImpl, err := helm.NewImpl(kubeToken, namespace) 62 | if err != nil { 63 | return nil, errors.WithMessage(err, "new helmClientImpl error: ") 64 | } 65 | pvcImpl := &PvcImpl{ 66 | HelmClientImpl: helmClientImpl, 67 | } 68 | return pvcImpl, nil 69 | } 70 | 71 | func (p *PvcImpl) Create(chartDir string, releaseName string, pvcInfo *PvcInfo) (*release.Release, error) { 72 | chartValues, err := p.parsePvcInfoToChartValues(pvcInfo) 73 | if err != nil { 74 | return nil, err 75 | } 76 | envChart, err := loader.LoadDir(chartDir) 77 | if err != nil { 78 | return nil, errors.Wrap(err, "loadDir error: "+utils.GetRuntimeLocation()) 79 | } 80 | client := action.NewInstall(p.ActionConfig) 81 | client.Namespace = *p.Config.Namespace 82 | client.ReleaseName = releaseName 83 | results, err := client.Run(envChart, chartValues) 84 | if err != nil { 85 | return nil, errors.Wrap(err, "Create run error: "+utils.GetRuntimeLocation()) 86 | } 87 | return results, nil 88 | } 89 | 90 | func (p *PvcImpl) Delete(releaseName string) error { 91 | client := action.NewUninstall(p.ActionConfig) 92 | _, err := client.Run(releaseName) 93 | if err != nil { 94 | return errors.Wrap(err, "Delete run error: "+utils.GetRuntimeLocation()) 95 | 96 | } 97 | return nil 98 | } 99 | 100 | func (p *PvcImpl) parsePvcInfoToChartValues(pvcInfo *PvcInfo) (map[string]interface{}, error) { 101 | chartValues := structs.Map(pvcInfo) 102 | return chartValues, nil 103 | } 104 | -------------------------------------------------------------------------------- /src/pineapple/handler/computing.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler 18 | 19 | import ( 20 | "github.com/4paradigm/openaios-platform/src/internal/billingclient" 21 | "github.com/4paradigm/openaios-platform/src/pineapple/apigen" 22 | "github.com/4paradigm/openaios-platform/src/pineapple/conf" 23 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 24 | "github.com/labstack/echo/v4" 25 | "github.com/pkg/errors" 26 | "net/http" 27 | ) 28 | 29 | func (handler *Handler) GetComputingUnitSpecs(ctx echo.Context) error { 30 | userID := ctx.Get("userID").(string) 31 | billingClient, err := billingclient.GetBillingClient(conf.GetBillingServerURL()) 32 | if err != nil { 33 | return echo.NewHTTPError( 34 | http.StatusInternalServerError, "cannot connect to billing server").SetInternal( 35 | errors.Wrap(err, utils.GetRuntimeLocation())) 36 | } 37 | computeunitInfos, err := billingclient.GetComputeUnitListByUserID(billingClient, userID) 38 | if err != nil { 39 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 40 | } 41 | result := make([]apigen.ComputeUnitSpec, len(computeunitInfos)) 42 | 43 | for i, info := range computeunitInfos { 44 | id := apigen.ComputeUnitId(*(info.Id)) 45 | result[i].Name = info.Id 46 | result[i].Id = &id 47 | result[i].Description = info.Description 48 | result[i].Price = info.Price 49 | } 50 | 51 | return ctx.JSON(http.StatusOK, result) 52 | } 53 | -------------------------------------------------------------------------------- /src/pineapple/handler/handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler provides handler. 18 | package handler 19 | 20 | type Handler struct { 21 | } 22 | -------------------------------------------------------------------------------- /src/pineapple/handler/models/charts.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 models provides models. 18 | package models 19 | 20 | type ImageInfo struct { 21 | Repository string `json:"repository" structs:"repository"` 22 | Tag string `json:"tag" structs:"tag"` 23 | PullPolicy string `json:"pullPolicy" structs:"pullPolicy"` 24 | } 25 | 26 | type MountInfo struct { 27 | MountPath string `json:"mountPath"` 28 | SubPath string `json:"subPath"` 29 | Name string `json:"name"` 30 | } 31 | 32 | type VolumeMounts []MountInfo 33 | -------------------------------------------------------------------------------- /src/pineapple/handler/others.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler 18 | 19 | import ( 20 | "context" 21 | "github.com/labstack/echo/v4" 22 | "github.com/pkg/errors" 23 | "github.com/4paradigm/openaios-platform/src/pineapple/apigen" 24 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 25 | "io" 26 | "net/http" 27 | "time" 28 | ) 29 | 30 | func (handler *Handler) GetContainerLog(ctx echo.Context, podName apigen.PodName, params apigen.GetContainerLogParams) error { 31 | userID := ctx.Get("userID").(string) 32 | kubeClient, err := utils.GetKubernetesClient() 33 | if err != nil { 34 | return echo.NewHTTPError( 35 | http.StatusInternalServerError, "cannot connect to kubernetes cluster").SetInternal( 36 | errors.Wrap(err, utils.GetRuntimeLocation())) 37 | } 38 | 39 | stream, err := utils.GetContainerLog(context.TODO(), kubeClient, userID, 40 | string(podName), (*string)(params.ContainerName), params.TailLines) 41 | if err != nil { 42 | return echo.NewHTTPError( 43 | http.StatusInternalServerError, "cannot get log").SetInternal( 44 | errors.Wrap(err, utils.GetRuntimeLocation())) 45 | } 46 | defer func() { 47 | err = stream.Close() 48 | if err != nil { 49 | ctx.Logger().Error(err) 50 | } 51 | }() 52 | 53 | ctx.Response().Header().Set(echo.HeaderContentType, "text/plain") 54 | ctx.Response().Header().Set("Connection", "keep-alive") 55 | ctx.Response().Header().Set("Access-Control-Allow-Origin", "*") 56 | ctx.Response().Header().Set("Access-Control-Allow-Methods", "*") 57 | ctx.Response().Header().Set("Transfer-Encoding", "chunked") 58 | ctx.Response().Header().Set("X-Content-Type-Options", "nosniff") 59 | ctx.Response().WriteHeader(http.StatusOK) 60 | 61 | for { 62 | buf := make([]byte, 2000) 63 | byteNum, err := stream.Read(buf) 64 | if err == io.EOF { 65 | break 66 | } 67 | if err != nil { 68 | return echo.NewHTTPError( 69 | http.StatusInternalServerError, "cannot read log").SetInternal( 70 | errors.Wrap(err, utils.GetRuntimeLocation())) 71 | } 72 | if byteNum == 0 { 73 | time.Sleep(1 * time.Second) 74 | continue 75 | } 76 | if _, err = ctx.Response().Writer.Write(buf); err != nil { 77 | return echo.NewHTTPError( 78 | http.StatusInternalServerError, "cannot read log").SetInternal( 79 | errors.Wrap(err, utils.GetRuntimeLocation())) 80 | } 81 | ctx.Response().Flush() 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /src/pineapple/handler/release.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler 18 | 19 | import ( 20 | "github.com/4paradigm/openaios-platform/src/internal/response" 21 | "github.com/4paradigm/openaios-platform/src/pineapple/apigen/internalapigen" 22 | "github.com/4paradigm/openaios-platform/src/pineapple/conf" 23 | "github.com/4paradigm/openaios-platform/src/pineapple/controller/application" 24 | "github.com/4paradigm/openaios-platform/src/pineapple/controller/environment" 25 | "github.com/4paradigm/openaios-platform/src/pineapple/utils/helm" 26 | "github.com/labstack/echo/v4" 27 | "net/http" 28 | ) 29 | 30 | func (handler *Handler) DeleteReleases(ctx echo.Context, params internalapigen.DeleteReleasesParams) error { 31 | if params.User == nil { 32 | return response.BadRequestWithMessagef(ctx, "request query user") 33 | } 34 | userID := *params.User 35 | bearerToken, err := conf.GetKubeToken() 36 | if err != nil { 37 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 38 | } 39 | helmImpl, err := helm.NewImpl(bearerToken, userID) 40 | if err != nil { 41 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 42 | } 43 | envs, err := helmImpl.List(environment.EnvPrefix) 44 | if err != nil { 45 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 46 | } 47 | apps, err := helmImpl.List(application.AppPrefix) 48 | if err != nil { 49 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 50 | } 51 | releases := append(envs, apps...) 52 | description := "ran out of credit" 53 | if err := helmImpl.DeleteListWithKeepHistory(releases, description); err != nil { 54 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 55 | } 56 | return response.StatusOKNoContent(ctx) 57 | } 58 | -------------------------------------------------------------------------------- /src/pineapple/handler/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler 18 | 19 | import ( 20 | "github.com/4paradigm/openaios-platform/src/internal/version" 21 | "github.com/labstack/echo/v4" 22 | "net/http" 23 | ) 24 | 25 | func (handler *Handler) PineappleVersion(c echo.Context) error { 26 | return c.String(http.StatusOK, version.GetVersion()) 27 | } 28 | -------------------------------------------------------------------------------- /src/pineapple/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | "github.com/4paradigm/openaios-platform/src/internal/auth" 23 | "github.com/4paradigm/openaios-platform/src/internal/response" 24 | "github.com/4paradigm/openaios-platform/src/internal/version" 25 | "github.com/4paradigm/openaios-platform/src/pineapple/apigen" 26 | "github.com/4paradigm/openaios-platform/src/pineapple/apigen/internalapigen" 27 | "github.com/4paradigm/openaios-platform/src/pineapple/handler" 28 | "github.com/labstack/echo-contrib/prometheus" 29 | "github.com/labstack/echo/v4" 30 | "github.com/labstack/echo/v4/middleware" 31 | "github.com/labstack/gommon/log" 32 | "net/http" 33 | "os" 34 | ) 35 | 36 | var ( 37 | staticDir = flag.String("static-dir", os.Getenv("PINEAPPLE_STATIC_DIR"), 38 | "static directory") 39 | ) 40 | 41 | func main() { 42 | flag.Parse() 43 | version.CheckVersionFlag() 44 | 45 | fmt.Printf("FLAGS:\n") 46 | flag.VisitAll(func(f *flag.Flag) { 47 | fmt.Printf("%-25v : %v\n", f.Name, f.Value.String()) 48 | }) 49 | 50 | e := echo.New() 51 | e.Logger.SetHeader(`${time_rfc3339} ${level} ${short_file}:${line} `) 52 | e.Logger.SetLevel(log.INFO) 53 | e.HTTPErrorHandler = response.CustomHTTPErrorHandler 54 | 55 | e.Use(middleware.Logger()) 56 | 57 | p := prometheus.NewPrometheus("echo", nil) 58 | p.Use(e) 59 | 60 | h := handler.Handler{} 61 | 62 | apiGroup := e.Group("/api") 63 | if *staticDir != "" { 64 | e.Logger.Info("using static dir") 65 | apiGroup.Static("/static", *staticDir) 66 | } 67 | 68 | if err := auth.InitAuth(); err != nil { 69 | panic(err.Error()) 70 | } 71 | apiGroup.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { 72 | c.Set("bearerToken", key) 73 | idTokenClaim, err := auth.Verify(key) 74 | if err != nil { 75 | c.Logger().Warn("auth verify failed, err = " + err.Error()) 76 | return false, err 77 | } 78 | c.Set("userName", idTokenClaim.PreferredUserName) 79 | c.Set("userID", idTokenClaim.Sub) 80 | return true, nil 81 | })) 82 | apiGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc { 83 | return func(c echo.Context) error { 84 | if c.Get("userID") == nil { 85 | return next(c) 86 | } 87 | if err := InitUser(c); err != nil { 88 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 89 | } 90 | return next(c) 91 | } 92 | }) 93 | 94 | apiGroup.GET("/userinfo", h.UserInfo) 95 | apigen.RegisterHandlers(apiGroup, &h) 96 | 97 | internalAPIGroup := e.Group("/internal-api") 98 | internalapigen.RegisterHandlers(internalAPIGroup, &h) 99 | 100 | e.Logger.Info(e.Start("0.0.0.0:80")) 101 | } 102 | -------------------------------------------------------------------------------- /src/pineapple/utils/helm/chart.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 helm provides utils for helm. 18 | package helm 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/pineapple/conf" 22 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 23 | "github.com/pkg/errors" 24 | "helm.sh/helm/v3/pkg/chart" 25 | "helm.sh/helm/v3/pkg/chart/loader" 26 | "net/http" 27 | ) 28 | 29 | func DownloadChart(url string) (*chart.Chart, error) { 30 | realURL, err := getChartrepoURL(url) 31 | if err != nil { 32 | return nil, errors.Wrap(err, "getChartrepoURL url error: "+utils.GetRuntimeLocation()) 33 | } 34 | client := new(http.Client) 35 | request, err := http.NewRequest("GET", realURL, nil) 36 | if err != nil { 37 | return nil, errors.Wrap(err, "NewRequest error: "+utils.GetRuntimeLocation()) 38 | } 39 | request.Header.Add("Accept-Encoding", "gzip") 40 | request.SetBasicAuth(conf.GetHarborAdmin()) 41 | resp, err := client.Do(request) 42 | if err != nil { 43 | return nil, errors.Wrap(err, "Get charts from url error: "+utils.GetRuntimeLocation()) 44 | } 45 | defer resp.Body.Close() 46 | chart, err := loader.LoadArchive(resp.Body) 47 | if err != nil { 48 | return nil, errors.Wrap(err, "LoadArchive from reader error: "+utils.GetRuntimeLocation()) 49 | } 50 | return chart, nil 51 | } 52 | 53 | func DownloadChartFiles(url string) ([]*chart.File, error) { 54 | realURL, err := getChartrepoURL(url) 55 | if err != nil { 56 | return nil, errors.Wrap(err, "getChartrepoURL url error: "+utils.GetRuntimeLocation()) 57 | } 58 | client := new(http.Client) 59 | request, err := http.NewRequest("GET", realURL, nil) 60 | if err != nil { 61 | return nil, errors.Wrap(err, "NewRequest error: "+utils.GetRuntimeLocation()) 62 | } 63 | request.Header.Add("Accept-Encoding", "gzip") 64 | request.SetBasicAuth(conf.GetHarborAdmin()) 65 | resp, err := client.Do(request) 66 | if err != nil { 67 | return nil, errors.Wrap(err, "Get charts from url error: "+utils.GetRuntimeLocation()) 68 | } 69 | defer resp.Body.Close() 70 | 71 | bufferedFiles, err := loader.LoadArchiveFiles(resp.Body) 72 | if err != nil { 73 | return nil, errors.Wrap(err, "LoadArchiveFiles error: "+utils.GetRuntimeLocation()) 74 | } 75 | files, err := expandCharts(bufferedFiles) 76 | if err != nil { 77 | return nil, errors.WithMessage(err, "expandCharts error: ") 78 | } 79 | return files, nil 80 | } 81 | 82 | func expandCharts(bufferedFiles []*loader.BufferedFile) ([]*chart.File, error) { 83 | var files []*chart.File 84 | for _, bf := range bufferedFiles { 85 | files = append(files, &chart.File{Name: bf.Name, Data: bf.Data}) 86 | } 87 | return files, nil 88 | } 89 | 90 | func getChartrepoURL(url string) (string, error) { 91 | // harborURL, _, _ := conf.GetHarborAddress() 92 | harborURL := conf.GetHarborURL() 93 | if harborURL == "" { 94 | return "", errors.New("harbor url is empty: " + utils.GetRuntimeLocation()) 95 | } 96 | realURL := harborURL + "/" + url 97 | return realURL, nil 98 | } 99 | -------------------------------------------------------------------------------- /src/pineapple/utils/helm/pineapple-info.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 helm provides utils for helm. 18 | package helm 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/internal/billingclient" 22 | "github.com/4paradigm/openaios-platform/src/pineapple/conf" 23 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 24 | "github.com/pkg/errors" 25 | ) 26 | 27 | type IPineappleInfo interface { 28 | SetValues(values map[string]interface{}) 29 | GetName() string 30 | GetUserID() string 31 | GetPrefix() string 32 | GetValues() map[string]interface{} 33 | CreateChartValues() (map[string]interface{}, error) 34 | } 35 | 36 | type PineappleInfo struct { 37 | Name string 38 | UserID string 39 | Prefix string 40 | Values map[string]interface{} 41 | } 42 | 43 | func NewPineappleInfo(name string, userID string, prefix string) (*PineappleInfo, error) { 44 | billingClient, err := billingclient.GetBillingClient(conf.GetBillingServerURL()) 45 | if err != nil { 46 | return nil, errors.Wrap(err, utils.GetRuntimeLocation()) 47 | } 48 | userBalance, err := billingclient.GetUserBalance(billingClient, userID) 49 | if err != nil { 50 | return nil, errors.WithMessage(err, "get GetUserBalance error: ") 51 | } 52 | if *userBalance <= 0.0 { 53 | return nil, errors.New("User does not have enough balance." + utils.GetRuntimeLocation()) 54 | } 55 | pineappleInfo := PineappleInfo{ 56 | Name: name, 57 | UserID: userID, 58 | Prefix: prefix, 59 | Values: nil, 60 | } 61 | return &pineappleInfo, nil 62 | } 63 | 64 | func (p *PineappleInfo) GetName() string { 65 | return p.Name 66 | } 67 | 68 | func (p *PineappleInfo) GetUserID() string { 69 | return p.UserID 70 | } 71 | 72 | func (p *PineappleInfo) GetPrefix() string { 73 | return p.Prefix 74 | } 75 | 76 | func (p *PineappleInfo) GetValues() map[string]interface{} { 77 | return p.Values 78 | } 79 | 80 | func (p *PineappleInfo) SetValues(values map[string]interface{}) { 81 | p.Values = values 82 | } 83 | 84 | func (p *PineappleInfo) CreateChartValues() (map[string]interface{}, error) { 85 | var chartValues = p.Values 86 | 87 | // Create appConf Values 88 | appConf, err := conf.GetAppConf() 89 | if err != nil { 90 | errors.WithMessage(err, "GetAppConf error: ") 91 | } 92 | chartValues["appConf"] = appConf 93 | 94 | return chartValues, nil 95 | } 96 | -------------------------------------------------------------------------------- /src/pineapple/utils/helm/postrenderer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 helm provides utils for helm. 18 | package helm 19 | 20 | import ( 21 | "bytes" 22 | "fmt" 23 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 24 | "github.com/pkg/errors" 25 | "path/filepath" 26 | "sigs.k8s.io/kustomize/api/filesys" 27 | "sigs.k8s.io/kustomize/api/konfig" 28 | ) 29 | import "sigs.k8s.io/kustomize/api/krusty" 30 | 31 | type PostRendererImpl struct { 32 | fSys filesys.FileSystem 33 | } 34 | 35 | func NewPostRendererImpl() *PostRendererImpl { 36 | return &PostRendererImpl{ 37 | fSys: filesys.MakeFsInMemory(), 38 | } 39 | } 40 | 41 | func (p *PostRendererImpl) WriteKustomzation(path string, content string) error { 42 | err := p.fSys.WriteFile( 43 | filepath.Join( 44 | path, 45 | konfig.DefaultKustomizationFileName()), []byte(` 46 | apiVersion: kustomize.config.k8s.io/v1beta1 47 | kind: Kustomization 48 | `+content)) 49 | if err != nil { 50 | return errors.Wrap(err, fmt.Sprintf("unexpected error while writing Kustomization to %s: ", path)+utils.GetRuntimeLocation()) 51 | } 52 | return nil 53 | } 54 | 55 | func (p *PostRendererImpl) WriteFile(path string, content string) error { 56 | err := p.fSys.WriteFile(path, []byte(content)) 57 | if err != nil { 58 | return errors.Wrap(err, fmt.Sprintf("unexpected error while writing file to %s: ", path)+utils.GetRuntimeLocation()) 59 | } 60 | return nil 61 | } 62 | 63 | func (p *PostRendererImpl) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { 64 | p.WriteFile("all.yaml", renderedManifests.String()) 65 | options := krusty.MakeDefaultOptions() 66 | kustomizer := krusty.MakeKustomizer(options) 67 | result, err := kustomizer.Run(p.fSys, ".") 68 | if err != nil { 69 | return nil, err 70 | } 71 | b, err := result.AsYaml() 72 | if err != nil { 73 | return nil, err 74 | } 75 | buf := bytes.NewBuffer(b) 76 | return buf, nil 77 | } 78 | -------------------------------------------------------------------------------- /src/pineapple/utils/helm/resource.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 helm provides utils for helm. 18 | package helm 19 | 20 | import ( 21 | "github.com/4paradigm/openaios-platform/src/pineapple/utils" 22 | "github.com/pkg/errors" 23 | v1 "k8s.io/api/core/v1" 24 | ) 25 | 26 | func (h *HelmClientImpl) GetPodList(helmReleaseInfo IHelmReleaseInfo) (*[]v1.Pod, error) { 27 | releaseName, err := helmReleaseInfo.GetReleaseName() 28 | if err != nil { 29 | return nil, errors.WithMessage(err, "GetReleaseName error: ") 30 | } 31 | //appType, err := helmReleaseInfo.GetType() 32 | if err != nil { 33 | return nil, errors.WithMessage(err, "GetType error: ") 34 | } 35 | client, err := utils.GetKubernetesClient() 36 | if err != nil { 37 | return nil, errors.WithMessage(err, "GetKubernetesClient error: ") 38 | } 39 | 40 | labelSelector := "openaios.4paradigm.com/app=true" + "," + "app.kubernetes.io/instance=" + releaseName 41 | 42 | podList, err := utils.GetPodList(client, labelSelector, *h.Config.Namespace) 43 | if err != nil { 44 | return nil, errors.WithMessage(err, "GetPodList error: ") 45 | } 46 | return podList, nil 47 | } 48 | 49 | func (h *HelmClientImpl) GetServiceList(helmReleaseInfo IHelmReleaseInfo) (*[]v1.Service, error) { 50 | releaseName, err := helmReleaseInfo.GetReleaseName() 51 | if err != nil { 52 | return nil, errors.WithMessage(err, "GetReleaseName error: ") 53 | } 54 | //appType, err := helmReleaseInfo.GetType() 55 | if err != nil { 56 | return nil, errors.WithMessage(err, "GetType error: ") 57 | } 58 | client, err := utils.GetKubernetesClient() 59 | if err != nil { 60 | return nil, errors.WithMessage(err, "GetKubernetesClient error: ") 61 | } 62 | 63 | labelSelector := "openaios.4paradigm.com/app=true" + "," + "app.kubernetes.io/instance=" + releaseName 64 | 65 | svcList, err := utils.GetServiceList(client, labelSelector, *h.Config.Namespace) 66 | if err != nil { 67 | return nil, errors.WithMessage(err, "GetPodList error: ") 68 | } 69 | return svcList, nil 70 | } 71 | 72 | func (h *HelmClientImpl) GetSpecifyInvolvedObjectEventList(involvedObjectName string) (*[]v1.Event, error) { 73 | client, err := utils.GetKubernetesClient() 74 | if err != nil { 75 | return nil, errors.WithMessage(err, "GetKubernetesClient error: ") 76 | } 77 | eventList, err := utils.GetSpecifyInvolvedObjectEventList(client, involvedObjectName, *h.Config.Namespace) 78 | if err != nil { 79 | return nil, errors.WithMessage(err, "GetSpecifyInvolvedObjectEventList error: ") 80 | } 81 | return eventList, nil 82 | } 83 | -------------------------------------------------------------------------------- /src/pineapple/utils/others.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 utils provides utils. 18 | package utils 19 | 20 | import ( 21 | "fmt" 22 | "math/rand" 23 | "os" 24 | "runtime" 25 | ) 26 | 27 | const codes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 28 | const codesLength = len(codes) 29 | 30 | func RandCode(n int) string { 31 | b := make([]byte, n) 32 | 33 | for i := range b { 34 | b[i] = codes[rand.Intn(codesLength)] 35 | } 36 | return string(b) 37 | } 38 | 39 | func GetEnvDefault(env, defaultValue string) string { 40 | v, exist := os.LookupEnv(env) 41 | if exist { 42 | return v 43 | } else { 44 | return defaultValue 45 | } 46 | } 47 | 48 | func GetRuntimeLocation() string { 49 | _, fn, line, _ := runtime.Caller(1) 50 | return fmt.Sprintf("%s:%d", fn, line) 51 | } 52 | -------------------------------------------------------------------------------- /src/staticcheck.conf: -------------------------------------------------------------------------------- 1 | checks = ["all"] 2 | initialisms = ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", 3 | "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", 4 | "IP", "JSON", "QPS", "RAM", "RPC", "SLA", 5 | "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", 6 | "UDP", "UI", "GID", "UID", "UUID", "URI", 7 | "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", 8 | "XSS", "SIP", "RTP", "AMQP", "DB", "TS"] 9 | dot_import_whitelist = [] 10 | http_status_code_whitelist = ["200", "400", "404", "500"] 11 | -------------------------------------------------------------------------------- /src/webhook/README.md: -------------------------------------------------------------------------------- 1 | # Webhook组件 2 | 3 | 为了方便用户使用我们指定的算力规格,我们使用MutatingAdmissionWebhook对用户创建的的任务进行修改。 4 | - 通过label`openaios.4paradigm.com/app: true`判断该pod为用户创建,然后读取其annotations,判断每个container所使用的算力规格,然后将每个container使用的算力规格作为list添加到annotation中。 5 | - 对于某个container使用的算力规格,我们通过请求billing server获取到算力规格的详细信息,然后加入到yaml中 6 | - 该模块参考了仓库 https://github.com/stackrox/admission-controller-webhook-demo 7 | - 为了保证webhook server的服务证书不过期,我们通过了`generate-keys.sh`生成了有效期36500天的证书,但是如果需要改变service name或者namespace,应对脚本进行修改并且重新生成证书。 -------------------------------------------------------------------------------- /src/webhook/generate-keys.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Copyright © 2021 peizhaoyou 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | # generate-keys.sh 20 | # 21 | # Generate a (self-signed) CA certificate and a certificate and private key to be used by the webhook demo server. 22 | # The certificate will be issued for the Common Name (CN) of `webhook-server.webhook-demo.svc`, which is the 23 | # cluster-internal DNS name for the service. 24 | # 25 | # NOTE: THIS SCRIPT EXISTS FOR DEMO PURPOSES ONLY. DO NOT USE IT FOR YOUR PRODUCTION WORKLOADS. 26 | 27 | : ${1?'missing key directory'} 28 | 29 | key_dir="$1" 30 | 31 | chmod 0700 "$key_dir" 32 | cd "$key_dir" 33 | 34 | cat >server.conf < ./SHA256SUMS 78 | 79 | release: 80 | ghr -c ${GIT_COMMIT} --delete --prerelease -u yudai -r gotty pre-release ${OUTPUT_DIR}/dist 81 | -------------------------------------------------------------------------------- /src/webterminal/gotty/backend/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 backend 18 | -------------------------------------------------------------------------------- /src/webterminal/gotty/backend/localcommand/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 localcommand provides an implementation of webtty.Slave 18 | // that launches a local command with a PTY. 19 | package localcommand 20 | -------------------------------------------------------------------------------- /src/webterminal/gotty/backend/localcommand/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 localcommand 18 | 19 | import ( 20 | "github.com/4paradigm/openaios-platform/src/webterminal/gotty/server" 21 | "syscall" 22 | "time" 23 | ) 24 | 25 | type Options struct { 26 | CloseSignal int `hcl:"close_signal" flagName:"close-signal" flagSName:"" flagDescribe:"Signal sent to the command process when gotty close it (default: SIGHUP)" default:"1"` 27 | CloseTimeout int `hcl:"close_timeout" flagName:"close-timeout" flagSName:"" flagDescribe:"Time in seconds to force kill process after client is disconnected (default: -1)" default:"-1"` 28 | } 29 | 30 | type Factory struct { 31 | command string 32 | argv []string 33 | options *Options 34 | opts []Option 35 | } 36 | 37 | func NewFactory(command string, argv []string, options *Options) (*Factory, error) { 38 | opts := []Option{WithCloseSignal(syscall.Signal(options.CloseSignal))} 39 | if options.CloseTimeout >= 0 { 40 | opts = append(opts, WithCloseTimeout(time.Duration(options.CloseTimeout)*time.Second)) 41 | } 42 | 43 | return &Factory{ 44 | command: command, 45 | argv: argv, 46 | options: options, 47 | opts: opts, 48 | }, nil 49 | } 50 | 51 | func (factory *Factory) Name() string { 52 | return "local command" 53 | } 54 | 55 | func (factory *Factory) New(params map[string][]string) (server.Slave, error) { 56 | argv := make([]string, len(factory.argv)) 57 | copy(argv, factory.argv) 58 | if params["arg"] != nil && len(params["arg"]) > 0 { 59 | argv = append(argv, params["arg"]...) 60 | } 61 | 62 | return New(factory.command, argv, factory.opts...) 63 | } 64 | -------------------------------------------------------------------------------- /src/webterminal/gotty/backend/localcommand/local_command.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 localcommand 18 | 19 | import ( 20 | "os" 21 | "os/exec" 22 | "syscall" 23 | "time" 24 | "unsafe" 25 | 26 | "github.com/creack/pty" 27 | "github.com/pkg/errors" 28 | ) 29 | 30 | const ( 31 | DefaultCloseSignal = syscall.SIGINT 32 | DefaultCloseTimeout = 10 * time.Second 33 | ) 34 | 35 | type LocalCommand struct { 36 | command string 37 | argv []string 38 | 39 | closeSignal syscall.Signal 40 | closeTimeout time.Duration 41 | 42 | cmd *exec.Cmd 43 | pty *os.File 44 | ptyClosed chan struct{} 45 | } 46 | 47 | func New(command string, argv []string, options ...Option) (*LocalCommand, error) { 48 | cmd := exec.Command(command, argv...) 49 | 50 | pty, err := pty.Start(cmd) 51 | if err != nil { 52 | // todo close cmd? 53 | return nil, errors.Wrapf(err, "failed to start command `%s`", command) 54 | } 55 | ptyClosed := make(chan struct{}) 56 | 57 | lcmd := &LocalCommand{ 58 | command: command, 59 | argv: argv, 60 | 61 | closeSignal: DefaultCloseSignal, 62 | closeTimeout: DefaultCloseTimeout, 63 | 64 | cmd: cmd, 65 | pty: pty, 66 | ptyClosed: ptyClosed, 67 | } 68 | 69 | for _, option := range options { 70 | option(lcmd) 71 | } 72 | 73 | // When the process is closed by the user, 74 | // close pty so that Read() on the pty breaks with an EOF. 75 | go func() { 76 | defer func() { 77 | lcmd.pty.Close() 78 | close(lcmd.ptyClosed) 79 | }() 80 | 81 | lcmd.cmd.Wait() 82 | }() 83 | 84 | return lcmd, nil 85 | } 86 | 87 | func (lcmd *LocalCommand) Read(p []byte) (n int, err error) { 88 | return lcmd.pty.Read(p) 89 | } 90 | 91 | func (lcmd *LocalCommand) Write(p []byte) (n int, err error) { 92 | return lcmd.pty.Write(p) 93 | } 94 | 95 | func (lcmd *LocalCommand) Close() error { 96 | if lcmd.cmd != nil && lcmd.cmd.Process != nil { 97 | lcmd.cmd.Process.Signal(lcmd.closeSignal) 98 | } 99 | for { 100 | select { 101 | case <-lcmd.ptyClosed: 102 | return nil 103 | case <-lcmd.closeTimeoutC(): 104 | lcmd.cmd.Process.Signal(syscall.SIGKILL) 105 | } 106 | } 107 | } 108 | 109 | func (lcmd *LocalCommand) WindowTitleVariables() map[string]interface{} { 110 | return map[string]interface{}{ 111 | "command": lcmd.command, 112 | "argv": lcmd.argv, 113 | "pid": lcmd.cmd.Process.Pid, 114 | } 115 | } 116 | 117 | func (lcmd *LocalCommand) ResizeTerminal(width int, height int) error { 118 | window := struct { 119 | row uint16 120 | col uint16 121 | x uint16 122 | y uint16 123 | }{ 124 | uint16(height), 125 | uint16(width), 126 | 0, 127 | 0, 128 | } 129 | _, _, errno := syscall.Syscall( 130 | syscall.SYS_IOCTL, 131 | lcmd.pty.Fd(), 132 | syscall.TIOCSWINSZ, 133 | uintptr(unsafe.Pointer(&window)), 134 | ) 135 | if errno != 0 { 136 | return errno 137 | } else { 138 | return nil 139 | } 140 | } 141 | 142 | func (lcmd *LocalCommand) closeTimeoutC() <-chan time.Time { 143 | if lcmd.closeTimeout >= 0 { 144 | return time.After(lcmd.closeTimeout) 145 | } 146 | 147 | return make(chan time.Time) 148 | } 149 | -------------------------------------------------------------------------------- /src/webterminal/gotty/backend/localcommand/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 localcommand 18 | 19 | import ( 20 | "syscall" 21 | "time" 22 | ) 23 | 24 | type Option func(*LocalCommand) 25 | 26 | func WithCloseSignal(signal syscall.Signal) Option { 27 | return func(lcmd *LocalCommand) { 28 | lcmd.closeSignal = signal 29 | } 30 | } 31 | 32 | func WithCloseTimeout(timeout time.Duration) Option { 33 | return func(lcmd *LocalCommand) { 34 | lcmd.closeTimeout = timeout 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/webterminal/gotty/cache/token/cache.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 token provides cache for token. 18 | package token 19 | 20 | import ( 21 | "github.com/pkg/errors" 22 | 23 | "time" 24 | ) 25 | 26 | //TtyParameter kubectl tty param 27 | type TtyParameter struct { 28 | Title string 29 | Arg []string 30 | } 31 | 32 | var ( 33 | InvalidToken = errors.New("ERROR:Invalid Token") 34 | NoTokenProvided = errors.New("ERROR:No Token Provided") 35 | ) 36 | 37 | // Cache interface that defines token cache behavior 38 | type Cache interface { 39 | Get(token string) *TtyParameter 40 | Delete(token string) error 41 | Add(token string, param *TtyParameter, d time.Duration) error 42 | } -------------------------------------------------------------------------------- /src/webterminal/gotty/cache/token/mem.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 token 18 | 19 | import ( 20 | "log" 21 | "time" 22 | 23 | "github.com/patrickmn/go-cache" 24 | ) 25 | 26 | //MemCache use memory to store token and TtyParameter 27 | type MemCache struct { 28 | cache *cache.Cache 29 | } 30 | 31 | //NewMemCache new MemCache 32 | func NewMemCache() *MemCache { 33 | return &MemCache{ 34 | cache: cache.New(5*time.Minute, 10*time.Minute), 35 | } 36 | } 37 | 38 | //Get token param from memory 39 | func (r *MemCache) Get(token string) *TtyParameter { 40 | obj, exit := r.cache.Get(token) 41 | if !exit { 42 | return nil 43 | } 44 | param, ok := obj.(TtyParameter) 45 | if ok { 46 | return ¶m 47 | } 48 | log.Printf("get token %s from mem obj is not tty param", token) 49 | return nil 50 | } 51 | 52 | //Delete token from memory 53 | func (r *MemCache) Delete(token string) error { 54 | r.cache.Delete(token) 55 | return nil 56 | } 57 | 58 | //Add token to memory 59 | func (r *MemCache) Add(token string, param *TtyParameter, d time.Duration) error { 60 | return r.cache.Add(token, *param, d) 61 | } 62 | -------------------------------------------------------------------------------- /src/webterminal/gotty/favicon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4paradigm/openaios-platform/c9dffe909f23fd73935cc75211b18c689a95b6a5/src/webterminal/gotty/favicon.psd -------------------------------------------------------------------------------- /src/webterminal/gotty/help.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 main 18 | 19 | var helpTemplate = `NAME: 20 | {{.Name}} - {{.Usage}} 21 | 22 | USAGE: 23 | {{.Name}} [options] [] 24 | 25 | VERSION: 26 | {{.Version}}{{if or .Author .Email}} 27 | 28 | AUTHOR:{{if .Author}} 29 | {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}} 30 | {{.Email}}{{end}}{{end}} 31 | 32 | OPTIONS: 33 | {{range .Flags}}{{.}} 34 | {{end}} 35 | ` 36 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/dist/hterm.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | import * as bare from "libapps"; 18 | export declare class Hterm { 19 | elem: HTMLElement; 20 | term: bare.hterm.Terminal; 21 | io: bare.hterm.IO; 22 | columns: number; 23 | rows: number; 24 | message: string; 25 | constructor(elem: HTMLElement); 26 | info(): { 27 | columns: number; 28 | rows: number; 29 | }; 30 | output(data: string): void; 31 | showMessage(message: string, timeout: number): void; 32 | removeMessage(): void; 33 | setWindowTitle(title: string): void; 34 | setPreferences(value: object): void; 35 | onInput(callback: (input: string) => void): void; 36 | onResize(callback: (colmuns: number, rows: number) => void): void; 37 | deactivate(): void; 38 | reset(): void; 39 | close(): void; 40 | } 41 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/dist/main.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/dist/websocket.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | export declare class ConnectionFactory { 18 | url: string; 19 | protocols: string[]; 20 | constructor(url: string, protocols: string[]); 21 | create(): Connection; 22 | } 23 | export declare class Connection { 24 | bare: WebSocket; 25 | constructor(url: string, protocols: string[]); 26 | open(): void; 27 | close(): void; 28 | send(data: string): void; 29 | isOpen(): boolean; 30 | onOpen(callback: () => void): void; 31 | onReceive(callback: (data: string) => void): void; 32 | onClose(callback: () => void): void; 33 | } 34 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/dist/webtty.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | export declare const protocols: string[]; 18 | export declare const msgInputUnknown = "0"; 19 | export declare const msgInput = "1"; 20 | export declare const msgPing = "2"; 21 | export declare const msgResizeTerminal = "3"; 22 | export declare const msgUnknownOutput = "0"; 23 | export declare const msgOutput = "1"; 24 | export declare const msgPong = "2"; 25 | export declare const msgSetWindowTitle = "3"; 26 | export declare const msgSetPreferences = "4"; 27 | export declare const msgSetReconnect = "5"; 28 | export interface Terminal { 29 | info(): { 30 | columns: number; 31 | rows: number; 32 | }; 33 | output(data: string): void; 34 | showMessage(message: string, timeout: number): void; 35 | removeMessage(): void; 36 | setWindowTitle(title: string): void; 37 | setPreferences(value: object): void; 38 | onInput(callback: (input: string) => void): void; 39 | onResize(callback: (colmuns: number, rows: number) => void): void; 40 | reset(): void; 41 | deactivate(): void; 42 | close(): void; 43 | } 44 | export interface Connection { 45 | open(): void; 46 | close(): void; 47 | send(data: string): void; 48 | isOpen(): boolean; 49 | onOpen(callback: () => void): void; 50 | onReceive(callback: (data: string) => void): void; 51 | onClose(callback: () => void): void; 52 | } 53 | export interface ConnectionFactory { 54 | create(): Connection; 55 | } 56 | export declare class WebTTY { 57 | term: Terminal; 58 | connectionFactory: ConnectionFactory; 59 | args: string; 60 | authToken: string; 61 | reconnect: number; 62 | constructor(term: Terminal, connectionFactory: ConnectionFactory, args: string, authToken: string); 63 | open(): () => void; 64 | } 65 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/dist/xterm.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | import * as bare from "dist/xterm"; 18 | import { lib } from "libapps"; 19 | export declare class Xterm { 20 | elem: HTMLElement; 21 | term: bare; 22 | resizeListener: () => void; 23 | decoder: lib.UTF8Decoder; 24 | message: HTMLElement; 25 | messageTimeout: number; 26 | messageTimer: number; 27 | constructor(elem: HTMLElement); 28 | info(): { 29 | columns: number; 30 | rows: number; 31 | }; 32 | output(data: string): void; 33 | showMessage(message: string, timeout: number): void; 34 | removeMessage(): void; 35 | setWindowTitle(title: string): void; 36 | setPreferences(value: object): void; 37 | onInput(callback: (input: string) => void): void; 38 | onResize(callback: (colmuns: number, rows: number) => void): void; 39 | deactivate(): void; 40 | reset(): void; 41 | close(): void; 42 | } 43 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/src/hterm.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | import * as bare from "libapps"; 18 | 19 | export class Hterm { 20 | elem: HTMLElement; 21 | 22 | term: bare.hterm.Terminal; 23 | io: bare.hterm.IO; 24 | 25 | columns: number; 26 | rows: number; 27 | 28 | // to "show" the current message when removeMessage() is called 29 | message: string; 30 | 31 | constructor(elem: HTMLElement) { 32 | this.elem = elem; 33 | bare.hterm.defaultStorage = new bare.lib.Storage.Memory(); 34 | this.term = new bare.hterm.Terminal(); 35 | this.term.getPrefs().set("send-encoding", "raw"); 36 | this.term.decorate(this.elem); 37 | 38 | this.io = this.term.io.push(); 39 | this.term.installKeyboard(); 40 | }; 41 | 42 | info(): { columns: number, rows: number } { 43 | return { columns: this.columns, rows: this.rows }; 44 | }; 45 | 46 | output(data: string) { 47 | if (this.term.io != null) { 48 | this.term.io.writeUTF8(data); 49 | } 50 | }; 51 | 52 | showMessage(message: string, timeout: number) { 53 | this.message = message; 54 | if (timeout > 0) { 55 | this.term.io.showOverlay(message, timeout); 56 | } else { 57 | this.term.io.showOverlay(message, null); 58 | } 59 | }; 60 | 61 | removeMessage(): void { 62 | // there is no hideOverlay(), so show the same message with 0 sec 63 | this.term.io.showOverlay(this.message, 0); 64 | } 65 | 66 | setWindowTitle(title: string) { 67 | this.term.setWindowTitle(title); 68 | }; 69 | 70 | setPreferences(value: object) { 71 | Object.keys(value).forEach((key) => { 72 | this.term.getPrefs().set(key, value[key]); 73 | }); 74 | }; 75 | 76 | onInput(callback: (input: string) => void) { 77 | this.io.onVTKeystroke = (data) => { 78 | callback(data); 79 | }; 80 | this.io.sendString = (data) => { 81 | callback(data); 82 | }; 83 | }; 84 | 85 | onResize(callback: (colmuns: number, rows: number) => void) { 86 | this.io.onTerminalResize = (columns: number, rows: number) => { 87 | this.columns = columns; 88 | this.rows = rows; 89 | callback(columns, rows); 90 | }; 91 | }; 92 | 93 | deactivate(): void { 94 | this.io.onVTKeystroke = function(){}; 95 | this.io.sendString = function(){}; 96 | this.io.onTerminalResize = function(){}; 97 | this.term.uninstallKeyboard(); 98 | } 99 | 100 | reset(): void { 101 | this.removeMessage(); 102 | this.term.installKeyboard(); 103 | } 104 | 105 | close(): void { 106 | this.term.uninstallKeyboard(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | import { Hterm } from "./hterm"; 18 | import { Xterm } from "./xterm"; 19 | import { Terminal, WebTTY, protocols } from "./webtty"; 20 | import { ConnectionFactory } from "./websocket"; 21 | 22 | // @TODO remove these 23 | declare var gotty_auth_token: string; 24 | declare var gotty_term: string; 25 | 26 | const elem = document.getElementById("terminal") 27 | 28 | if (elem !== null) { 29 | var term: Terminal; 30 | if (gotty_term == "hterm") { 31 | term = new Hterm(elem); 32 | } else { 33 | term = new Xterm(elem); 34 | } 35 | const httpsEnabled = window.location.protocol == "https:"; 36 | const url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws'; 37 | const args = window.location.search; 38 | const factory = new ConnectionFactory(url, protocols); 39 | const wt = new WebTTY(term, factory, args, gotty_auth_token); 40 | const closer = wt.open(); 41 | 42 | window.addEventListener("unload", () => { 43 | closer(); 44 | term.close(); 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/src/websocket.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | export class ConnectionFactory { 18 | url: string; 19 | protocols: string[]; 20 | 21 | constructor(url: string, protocols: string[]) { 22 | this.url = url; 23 | this.protocols = protocols; 24 | }; 25 | 26 | create(): Connection { 27 | return new Connection(this.url, this.protocols); 28 | }; 29 | } 30 | 31 | export class Connection { 32 | bare: WebSocket; 33 | 34 | 35 | constructor(url: string, protocols: string[]) { 36 | this.bare = new WebSocket(url, protocols); 37 | } 38 | 39 | open() { 40 | // nothing todo for websocket 41 | }; 42 | 43 | close() { 44 | this.bare.close(); 45 | }; 46 | 47 | send(data: string) { 48 | this.bare.send(data); 49 | }; 50 | 51 | isOpen(): boolean { 52 | if (this.bare.readyState == WebSocket.CONNECTING || 53 | this.bare.readyState == WebSocket.OPEN) { 54 | return true 55 | } 56 | return false 57 | } 58 | 59 | onOpen(callback: () => void) { 60 | this.bare.onopen = (event) => { 61 | callback(); 62 | } 63 | }; 64 | 65 | onReceive(callback: (data: string) => void) { 66 | this.bare.onmessage = (event) => { 67 | callback(event.data); 68 | } 69 | }; 70 | 71 | onClose(callback: () => void) { 72 | this.bare.onclose = (event) => { 73 | callback(); 74 | }; 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/src/xterm.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | import * as bare from "src/xterm"; 18 | import { lib } from "libapps" 19 | 20 | 21 | bare.loadAddon("fit"); 22 | 23 | export class Xterm { 24 | elem: HTMLElement; 25 | term: bare; 26 | resizeListener: () => void; 27 | decoder: lib.UTF8Decoder; 28 | 29 | message: HTMLElement; 30 | messageTimeout: number; 31 | messageTimer: number; 32 | 33 | 34 | constructor(elem: HTMLElement) { 35 | this.elem = elem; 36 | this.term = new bare(); 37 | 38 | this.message = elem.ownerDocument.createElement("div"); 39 | this.message.className = "xterm-overlay"; 40 | this.messageTimeout = 2000; 41 | 42 | this.resizeListener = () => { 43 | this.term.fit(); 44 | this.term.scrollToBottom(); 45 | this.showMessage(String(this.term.cols) + "x" + String(this.term.rows), this.messageTimeout); 46 | }; 47 | 48 | this.term.on("open", () => { 49 | this.resizeListener(); 50 | window.addEventListener("resize", () => { this.resizeListener(); }); 51 | }); 52 | 53 | this.term.open(elem, true); 54 | 55 | this.decoder = new lib.UTF8Decoder() 56 | }; 57 | 58 | info(): { columns: number, rows: number } { 59 | return { columns: this.term.cols, rows: this.term.rows }; 60 | }; 61 | 62 | output(data: string) { 63 | this.term.write(this.decoder.decode(data)); 64 | }; 65 | 66 | showMessage(message: string, timeout: number) { 67 | this.message.textContent = message; 68 | this.elem.appendChild(this.message); 69 | 70 | if (this.messageTimer) { 71 | clearTimeout(this.messageTimer); 72 | } 73 | if (timeout > 0) { 74 | this.messageTimer = setTimeout(() => { 75 | this.elem.removeChild(this.message); 76 | }, timeout); 77 | } 78 | }; 79 | 80 | removeMessage(): void { 81 | if (this.message.parentNode == this.elem) { 82 | this.elem.removeChild(this.message); 83 | } 84 | } 85 | 86 | setWindowTitle(title: string) { 87 | document.title = title; 88 | }; 89 | 90 | setPreferences(value: object) { 91 | }; 92 | 93 | onInput(callback: (input: string) => void) { 94 | this.term.on("data", (data) => { 95 | callback(data); 96 | }); 97 | 98 | }; 99 | 100 | onResize(callback: (colmuns: number, rows: number) => void) { 101 | this.term.on("resize", (data) => { 102 | callback(data.cols, data.rows); 103 | }); 104 | }; 105 | 106 | deactivate(): void { 107 | this.term.off("data"); 108 | this.term.off("resize"); 109 | this.term.blur(); 110 | } 111 | 112 | reset(): void { 113 | this.removeMessage(); 114 | this.term.clear(); 115 | } 116 | 117 | close(): void { 118 | window.removeEventListener("resize", this.resizeListener); 119 | this.term.destroy(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictNullChecks": true, 4 | "noUnusedLocals" : true, 5 | "noImplicitThis": true, 6 | "alwaysStrict": true, 7 | "outDir": "./dist/", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "target": "es5", 11 | "module": "commonJS", 12 | "baseUrl": "./", 13 | "paths": { 14 | "*": ["./typings/*"] 15 | } 16 | }, 17 | "exclude": [ 18 | "node_modules" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/typings/libapps/index.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | export declare namespace hterm { 18 | export class Terminal { 19 | io: IO; 20 | onTerminalReady: () => void; 21 | 22 | constructor(); 23 | getPrefs(): Prefs; 24 | decorate(HTMLElement); 25 | installKeyboard(): void; 26 | uninstallKeyboard(): void; 27 | setWindowTitle(title: string): void; 28 | reset(): void; 29 | softReset(): void; 30 | } 31 | 32 | export class IO { 33 | writeUTF8: ((data: string) => void); 34 | writeUTF16: ((data: string) => void); 35 | onVTKeystroke: ((data: string) => void) | null; 36 | sendString: ((data: string) => void) | null; 37 | onTerminalResize: ((columns: number, rows: number) => void) | null; 38 | 39 | push(): IO; 40 | writeUTF(data: string); 41 | showOverlay(message: string, timeout: number | null); 42 | } 43 | 44 | export class Prefs { 45 | set(key: string, value: string): void; 46 | } 47 | 48 | export var defaultStorage: lib.Storage; 49 | } 50 | 51 | export declare namespace lib { 52 | export interface Storage { 53 | } 54 | 55 | export interface Memory { 56 | new (): Storage; 57 | Memory(): Storage 58 | } 59 | 60 | export var Storage: { 61 | Memory: Memory 62 | } 63 | 64 | export class UTF8Decoder { 65 | decode(str: string) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/webterminal/gotty/js/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 18 | 19 | module.exports = { 20 | entry: "./src/main.ts", 21 | output: { 22 | filename: "./dist/gotty-bundle.js" 23 | }, 24 | devtool: "source-map", 25 | resolve: { 26 | extensions: [".ts", ".tsx", ".js"], 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.tsx?$/, 32 | loader: "ts-loader", 33 | exclude: /node_modules/ 34 | }, 35 | { 36 | test: /\.js$/, 37 | include: /node_modules/, 38 | loader: 'license-loader' 39 | } 40 | ] 41 | }, 42 | plugins: [ 43 | new UglifyJSPlugin() 44 | ] 45 | }; 46 | -------------------------------------------------------------------------------- /src/webterminal/gotty/pkg/homedir/expand.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 homedir 18 | 19 | import ( 20 | "os" 21 | ) 22 | 23 | func Expand(path string) string { 24 | if path[0:2] == "~/" { 25 | return os.Getenv("HOME") + path[1:] 26 | } else { 27 | return path 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/webterminal/gotty/pkg/randomstring/generate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 randomstring 18 | 19 | import ( 20 | "crypto/rand" 21 | "math/big" 22 | "strconv" 23 | ) 24 | 25 | func Generate(length int) string { 26 | const base = 36 27 | size := big.NewInt(base) 28 | n := make([]byte, length) 29 | for i, _ := range n { 30 | c, _ := rand.Int(rand.Reader, size) 31 | n[i] = strconv.FormatInt(c.Int64(), base)[0] 32 | } 33 | return string(n) 34 | } 35 | -------------------------------------------------------------------------------- /src/webterminal/gotty/resources/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4paradigm/openaios-platform/c9dffe909f23fd73935cc75211b18c689a95b6a5/src/webterminal/gotty/resources/favicon.png -------------------------------------------------------------------------------- /src/webterminal/gotty/resources/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | html, body, #terminal { 18 | background: black; 19 | height: 100%; 20 | width: 100%; 21 | padding: 0%; 22 | margin: 0%; 23 | } -------------------------------------------------------------------------------- /src/webterminal/gotty/resources/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | {{ .title }} 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/webterminal/gotty/resources/xterm_customize.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 | .terminal { 18 | font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, Menlo, Terminal, monospace, "Apple Symbols"; 19 | } 20 | 21 | .xterm-overlay { 22 | font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, Menlo, Terminal, monospace, "Apple Symbols"; 23 | border-radius: 15px; 24 | font-size: xx-large; 25 | color: black; 26 | background: white; 27 | opacity: 0.75; 28 | padding: 0.2em 0.5em 0.2em 0.5em; 29 | position: absolute; 30 | top: 50%; 31 | left: 50%; 32 | transform: translate(-50%, -50%); 33 | user-select: none; 34 | transition: opacity 180ms ease-in; 35 | } -------------------------------------------------------------------------------- /src/webterminal/gotty/screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4paradigm/openaios-platform/c9dffe909f23fd73935cc75211b18c689a95b6a5/src/webterminal/gotty/screenshot.gif -------------------------------------------------------------------------------- /src/webterminal/gotty/server/handler_atomic.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | import ( 20 | "sync" 21 | "time" 22 | ) 23 | 24 | type counter struct { 25 | duration time.Duration 26 | zeroTimer *time.Timer 27 | wg sync.WaitGroup 28 | connections int 29 | mutex sync.Mutex 30 | } 31 | 32 | func newCounter(duration time.Duration) *counter { 33 | zeroTimer := time.NewTimer(duration) 34 | 35 | // when duration is 0, drain the expire event here 36 | // so that user will never get the event. 37 | if duration == 0 { 38 | <-zeroTimer.C 39 | } 40 | 41 | return &counter{ 42 | duration: duration, 43 | zeroTimer: zeroTimer, 44 | } 45 | } 46 | 47 | func (counter *counter) add(n int) int { 48 | counter.mutex.Lock() 49 | defer counter.mutex.Unlock() 50 | 51 | if counter.duration > 0 { 52 | counter.zeroTimer.Stop() 53 | } 54 | counter.wg.Add(n) 55 | counter.connections += n 56 | 57 | return counter.connections 58 | } 59 | 60 | func (counter *counter) done() int { 61 | counter.mutex.Lock() 62 | defer counter.mutex.Unlock() 63 | 64 | counter.connections-- 65 | counter.wg.Done() 66 | if counter.connections == 0 && counter.duration > 0 { 67 | counter.zeroTimer.Reset(counter.duration) 68 | } 69 | 70 | return counter.connections 71 | } 72 | 73 | func (counter *counter) count() int { 74 | counter.mutex.Lock() 75 | defer counter.mutex.Unlock() 76 | 77 | return counter.connections 78 | } 79 | 80 | func (counter *counter) wait() { 81 | counter.wg.Wait() 82 | } 83 | 84 | func (counter *counter) timer() *time.Timer { 85 | return counter.zeroTimer 86 | } 87 | -------------------------------------------------------------------------------- /src/webterminal/gotty/server/init_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | type InitMessage struct { 20 | Arguments string `json:"Arguments,omitempty"` 21 | AuthToken string `json:"AuthToken,omitempty"` 22 | } 23 | -------------------------------------------------------------------------------- /src/webterminal/gotty/server/list_address.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | import ( 20 | "net" 21 | ) 22 | 23 | func listAddresses() (addresses []string) { 24 | ifaces, err := net.Interfaces() 25 | if err != nil { 26 | return []string{} 27 | } 28 | 29 | addresses = make([]string, 0, len(ifaces)) 30 | 31 | for _, iface := range ifaces { 32 | ifAddrs, _ := iface.Addrs() 33 | for _, ifAddr := range ifAddrs { 34 | switch v := ifAddr.(type) { 35 | case *net.IPNet: 36 | addresses = append(addresses, v.IP.String()) 37 | case *net.IPAddr: 38 | addresses = append(addresses, v.IP.String()) 39 | } 40 | } 41 | } 42 | 43 | return addresses 44 | } 45 | -------------------------------------------------------------------------------- /src/webterminal/gotty/server/log_response_writer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | import ( 20 | "bufio" 21 | "net" 22 | "net/http" 23 | ) 24 | 25 | type logResponseWriter struct { 26 | http.ResponseWriter 27 | status int 28 | } 29 | 30 | func (w *logResponseWriter) WriteHeader(status int) { 31 | w.status = status 32 | w.ResponseWriter.WriteHeader(status) 33 | } 34 | 35 | func (w *logResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 36 | hj, _ := w.ResponseWriter.(http.Hijacker) 37 | w.status = http.StatusSwitchingProtocols 38 | return hj.Hijack() 39 | } 40 | -------------------------------------------------------------------------------- /src/webterminal/gotty/server/middleware.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | import ( 20 | "encoding/base64" 21 | "log" 22 | "net/http" 23 | "strings" 24 | ) 25 | 26 | func (server *Server) wrapLogger(handler http.Handler) http.Handler { 27 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 28 | rw := &logResponseWriter{w, 200} 29 | handler.ServeHTTP(rw, r) 30 | log.Printf("%s %d %s %s", r.RemoteAddr, rw.status, r.Method, r.URL.Path) 31 | }) 32 | } 33 | 34 | func (server *Server) wrapHeaders(handler http.Handler) http.Handler { 35 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 36 | // todo add version 37 | w.Header().Set("Server", "GoTTY") 38 | handler.ServeHTTP(w, r) 39 | }) 40 | } 41 | 42 | func (server *Server) wrapBasicAuth(handler http.Handler, credential string) http.Handler { 43 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 44 | token := strings.SplitN(r.Header.Get("Authorization"), " ", 2) 45 | 46 | if len(token) != 2 || strings.ToLower(token[0]) != "basic" { 47 | w.Header().Set("WWW-Authenticate", `Basic realm="GoTTY"`) 48 | http.Error(w, "Bad Request", http.StatusUnauthorized) 49 | return 50 | } 51 | 52 | payload, err := base64.StdEncoding.DecodeString(token[1]) 53 | if err != nil { 54 | http.Error(w, "Internal Server Error", http.StatusInternalServerError) 55 | return 56 | } 57 | 58 | if credential != string(payload) { 59 | w.Header().Set("WWW-Authenticate", `Basic realm="GoTTY"`) 60 | http.Error(w, "authorization failed", http.StatusUnauthorized) 61 | return 62 | } 63 | 64 | log.Printf("Basic Authentication Succeeded: %s", r.RemoteAddr) 65 | handler.ServeHTTP(w, r) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /src/webterminal/gotty/server/run_option.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | import ( 20 | "context" 21 | ) 22 | 23 | // RunOptions holds a set of configurations for Server.Run(). 24 | type RunOptions struct { 25 | gracefullCtx context.Context 26 | } 27 | 28 | // RunOption is an option of Server.Run(). 29 | type RunOption func(*RunOptions) 30 | 31 | // WithGracefullContext accepts a context to shutdown a Server 32 | // with care for existing client connections. 33 | func WithGracefullContext(ctx context.Context) RunOption { 34 | return func(options *RunOptions) { 35 | options.gracefullCtx = ctx 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/webterminal/gotty/server/slave.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | import ( 20 | "github.com/4paradigm/openaios-platform/src/webterminal/gotty/webtty" 21 | ) 22 | 23 | // Slave is webtty.Slave with some additional methods. 24 | type Slave interface { 25 | webtty.Slave 26 | 27 | Close() error 28 | } 29 | 30 | type Factory interface { 31 | Name() string 32 | New(params map[string][]string) (Slave, error) 33 | } 34 | -------------------------------------------------------------------------------- /src/webterminal/gotty/server/ws_wrapper.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 server 18 | 19 | import ( 20 | "github.com/gorilla/websocket" 21 | ) 22 | 23 | type wsWrapper struct { 24 | *websocket.Conn 25 | } 26 | 27 | func (wsw *wsWrapper) Write(p []byte) (n int, err error) { 28 | writer, err := wsw.Conn.NextWriter(websocket.TextMessage) 29 | if err != nil { 30 | return 0, err 31 | } 32 | defer writer.Close() 33 | return writer.Write(p) 34 | } 35 | 36 | func (wsw *wsWrapper) Read(p []byte) (n int, err error) { 37 | for { 38 | msgType, reader, err := wsw.Conn.NextReader() 39 | if err != nil { 40 | return 0, err 41 | } 42 | 43 | if msgType != websocket.TextMessage { 44 | continue 45 | } 46 | 47 | return reader.Read(p) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/webterminal/gotty/staticcheck.conf: -------------------------------------------------------------------------------- 1 | checks = ["all", "-ST1000", "-ST1005", "-ST1003", "-SA9002", "-ST1013", "-S1005", "-SA4006"] 2 | initialisms = ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", 3 | "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", 4 | "IP", "JSON", "QPS", "RAM", "RPC", "SLA", 5 | "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", 6 | "UDP", "UI", "GID", "UID", "UUID", "URI", 7 | "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", 8 | "XSS", "SIP", "RTP", "AMQP", "DB", "TS"] 9 | dot_import_whitelist = [] 10 | http_status_code_whitelist = ["200", "400", "404", "500"] 11 | -------------------------------------------------------------------------------- /src/webterminal/gotty/utils/default.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 utils 18 | 19 | import ( 20 | "fmt" 21 | "github.com/fatih/structs" 22 | "reflect" 23 | "strconv" 24 | ) 25 | 26 | func ApplyDefaultValues(struct_ interface{}) (err error) { 27 | o := structs.New(struct_) 28 | 29 | for _, field := range o.Fields() { 30 | defaultValue := field.Tag("default") 31 | if defaultValue == "" { 32 | continue 33 | } 34 | var val interface{} 35 | switch field.Kind() { 36 | case reflect.String: 37 | val = defaultValue 38 | case reflect.Bool: 39 | if defaultValue == "true" { 40 | val = true 41 | } else if defaultValue == "false" { 42 | val = false 43 | } else { 44 | return fmt.Errorf("invalid bool expression: %v, use true/false", defaultValue) 45 | } 46 | case reflect.Int: 47 | val, err = strconv.Atoi(defaultValue) 48 | if err != nil { 49 | return err 50 | } 51 | default: 52 | val = field.Value() 53 | } 54 | field.Set(val) 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /src/webterminal/gotty/utils/flags.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 utils 18 | 19 | import ( 20 | "reflect" 21 | "strings" 22 | 23 | "github.com/fatih/structs" 24 | "github.com/urfave/cli/v2" 25 | ) 26 | 27 | func GenerateFlags(options ...interface{}) (flags []cli.Flag, mappings map[string]string, err error) { 28 | mappings = make(map[string]string) 29 | 30 | for _, struct_ := range options { 31 | o := structs.New(struct_) 32 | for _, field := range o.Fields() { 33 | flagName := field.Tag("flagName") 34 | if flagName == "" { 35 | continue 36 | } 37 | envName := "GOTTY_" + strings.ToUpper(strings.Join(strings.Split(flagName, "-"), "_")) 38 | mappings[flagName] = field.Name() 39 | 40 | flagShortName := field.Tag("flagSName") 41 | if flagShortName != "" { 42 | flagName += ", " + flagShortName 43 | } 44 | 45 | flagDescription := field.Tag("flagDescribe") 46 | 47 | switch field.Kind() { 48 | case reflect.String: 49 | flags = append(flags, &cli.StringFlag{ 50 | Name: flagName, 51 | Value: field.Value().(string), 52 | Usage: flagDescription, 53 | EnvVars: []string{envName}, 54 | }) 55 | case reflect.Bool: 56 | flags = append(flags, &cli.BoolFlag{ 57 | Name: flagName, 58 | Usage: flagDescription, 59 | EnvVars: []string{envName}, 60 | }) 61 | case reflect.Int: 62 | flags = append(flags, &cli.IntFlag{ 63 | Name: flagName, 64 | Value: field.Value().(int), 65 | Usage: flagDescription, 66 | EnvVars: []string{envName}, 67 | }) 68 | } 69 | } 70 | } 71 | 72 | return 73 | } 74 | 75 | func ApplyFlags( 76 | flags []cli.Flag, 77 | mappingHint map[string]string, 78 | c *cli.Context, 79 | options ...interface{}, 80 | ) { 81 | objects := make([]*structs.Struct, len(options)) 82 | for i, struct_ := range options { 83 | objects[i] = structs.New(struct_) 84 | } 85 | 86 | for flagName, fieldName := range mappingHint { 87 | if !c.IsSet(flagName) { 88 | continue 89 | } 90 | var field *structs.Field 91 | var ok bool 92 | for _, o := range objects { 93 | field, ok = o.FieldOk(fieldName) 94 | if ok { 95 | break 96 | } 97 | } 98 | if field == nil { 99 | continue 100 | } 101 | var val interface{} 102 | switch field.Kind() { 103 | case reflect.String: 104 | val = c.String(flagName) 105 | case reflect.Bool: 106 | val = c.Bool(flagName) 107 | case reflect.Int: 108 | val = c.Int(flagName) 109 | } 110 | field.Set(val) 111 | } 112 | } 113 | 114 | //func ApplyConfigFile(filePath string, options ...interface{}) error { 115 | // filePath = homedir.Expand(filePath) 116 | // if _, err := os.Stat(filePath); os.IsNotExist(err) { 117 | // return err 118 | // } 119 | // 120 | // fileString := []byte{} 121 | // log.Printf("Loading config file at: %s", filePath) 122 | // fileString, err := ioutil.ReadFile(filePath) 123 | // if err != nil { 124 | // return err 125 | // } 126 | // 127 | // for _, object := range options { 128 | // if err := hcl.Decode(object, string(fileString)); err != nil { 129 | // return err 130 | // } 131 | // } 132 | // 133 | // return nil 134 | //} 135 | -------------------------------------------------------------------------------- /src/webterminal/gotty/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 main 18 | 19 | var Version = "unknown_version" 20 | var CommitID = "unknown_commit" 21 | -------------------------------------------------------------------------------- /src/webterminal/gotty/webtty/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 webtty provides a protocl and an implementation to 18 | // controll terminals thorough networks. 19 | package webtty 20 | -------------------------------------------------------------------------------- /src/webterminal/gotty/webtty/errors.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 webtty 18 | 19 | import ( 20 | "errors" 21 | ) 22 | 23 | var ( 24 | // ErrSlaveClosed indicates the function has exited by the slave 25 | ErrSlaveClosed = errors.New("slave closed") 26 | 27 | // ErrSlaveClosed is returned when the slave connection is closed. 28 | ErrMasterClosed = errors.New("master closed") 29 | ) 30 | -------------------------------------------------------------------------------- /src/webterminal/gotty/webtty/master.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 webtty 18 | 19 | import ( 20 | "io" 21 | ) 22 | 23 | // Master represents a PTY master, usually it's a websocket connection. 24 | type Master io.ReadWriter 25 | -------------------------------------------------------------------------------- /src/webterminal/gotty/webtty/message_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 webtty 18 | 19 | // Protocols defines the name of this protocol, 20 | // which is supposed to be used to the subprotocol of Websockt streams. 21 | var Protocols = []string{"webtty"} 22 | 23 | const ( 24 | // Unknown message type, maybe sent by a bug 25 | UnknownInput = '0' 26 | // User input typically from a keyboard 27 | Input = '1' 28 | // Ping to the server 29 | Ping = '2' 30 | // Notify that the browser size has been changed 31 | ResizeTerminal = '3' 32 | ) 33 | 34 | const ( 35 | // Unknown message type, maybe set by a bug 36 | UnknownOutput = '0' 37 | // Normal output to the terminal 38 | Output = '1' 39 | // Pong to the browser 40 | Pong = '2' 41 | // Set window title of the terminal 42 | SetWindowTitle = '3' 43 | // Set terminal preference 44 | SetPreferences = '4' 45 | // Make terminal to reconnect 46 | SetReconnect = '5' 47 | ) 48 | -------------------------------------------------------------------------------- /src/webterminal/gotty/webtty/option.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 webtty 18 | 19 | import ( 20 | "encoding/json" 21 | 22 | "github.com/pkg/errors" 23 | ) 24 | 25 | // Option is an option for WebTTY. 26 | type Option func(*WebTTY) error 27 | 28 | // WithPermitWrite sets a WebTTY to accept input from slaves. 29 | func WithPermitWrite() Option { 30 | return func(wt *WebTTY) error { 31 | wt.permitWrite = true 32 | return nil 33 | } 34 | } 35 | 36 | // WithFixedColumns sets a fixed width to TTY master. 37 | func WithFixedColumns(columns int) Option { 38 | return func(wt *WebTTY) error { 39 | wt.columns = columns 40 | return nil 41 | } 42 | } 43 | 44 | // WithFixedRows sets a fixed height to TTY master. 45 | func WithFixedRows(rows int) Option { 46 | return func(wt *WebTTY) error { 47 | wt.rows = rows 48 | return nil 49 | } 50 | } 51 | 52 | // WithWindowTitle sets the default window title of the session 53 | func WithWindowTitle(windowTitle []byte) Option { 54 | return func(wt *WebTTY) error { 55 | wt.windowTitle = windowTitle 56 | return nil 57 | } 58 | } 59 | 60 | // WithReconnect enables reconnection on the master side. 61 | func WithReconnect(timeInSeconds int) Option { 62 | return func(wt *WebTTY) error { 63 | wt.reconnect = timeInSeconds 64 | return nil 65 | } 66 | } 67 | 68 | // WithMasterPreferences sets an optional configuration of master. 69 | func WithMasterPreferences(preferences interface{}) Option { 70 | return func(wt *WebTTY) error { 71 | prefs, err := json.Marshal(preferences) 72 | if err != nil { 73 | return errors.Wrapf(err, "failed to marshal preferences as JSON") 74 | } 75 | wt.masterPrefs = prefs 76 | return nil 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/webterminal/gotty/webtty/slave.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 webtty 18 | 19 | import ( 20 | "io" 21 | ) 22 | 23 | // Slave represents a PTY slave, typically it's a local command. 24 | type Slave interface { 25 | io.ReadWriter 26 | 27 | // WindowTitleVariables returns any values that can be used to fill out 28 | // the title of a terminal. 29 | WindowTitleVariables() map[string]interface{} 30 | 31 | // ResizeTerminal sets a new size of the terminal. 32 | ResizeTerminal(columns int, rows int) error 33 | } 34 | -------------------------------------------------------------------------------- /src/webterminal/gotty/wercker.yml: -------------------------------------------------------------------------------- 1 | box: golang:1.9.0 2 | 3 | build: 4 | steps: 5 | - setup-go-workspace 6 | - script: 7 | name: tools 8 | code: make tools 9 | - script: 10 | name: test 11 | code: make test 12 | - script: 13 | name: cross compile 14 | code: make cross_compile OUTPUT_DIR=$WERCKER_OUTPUT_DIR 15 | - script: 16 | name: store Makefile 17 | code: cp Makefile $WERCKER_OUTPUT_DIR/ 18 | 19 | deploy: 20 | steps: 21 | - script: 22 | name: tools 23 | code: make tools 24 | - script: 25 | name: targz 26 | code: make targz OUTPUT_DIR=. 27 | - script: 28 | name: shasums 29 | code: make shasums OUTPUT_DIR=. 30 | - script: 31 | name: release 32 | code: make release OUTPUT_DIR=. GIT_COMMIT=$WERCKER_GIT_COMMIT 33 | -------------------------------------------------------------------------------- /src/webterminal/server/controller/webterminal/webterminal.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 webterminal provides controller for webterminal. 18 | package webterminal 19 | 20 | import ( 21 | "bytes" 22 | "encoding/json" 23 | "github.com/pkg/errors" 24 | "io/ioutil" 25 | "net/http" 26 | ) 27 | 28 | type Webterminal struct { 29 | Namespace string `json:"namespace"` 30 | PodName string `json:"pod_name"` 31 | ContainerName string `json:"container_name"` 32 | BearerToken string `json:"bearer_token"` 33 | } 34 | 35 | type GetTerminalAPIResponse struct { 36 | Success bool `json:"success"` 37 | Token string `json:"token"` 38 | Message string `json:"message"` 39 | } 40 | 41 | func GetWebterminal(terminalInfo Webterminal) (string, error) { 42 | reqBody, err := json.Marshal(terminalInfo) 43 | if err != nil { 44 | return "", errors.Wrap(err, "Marshal terminalInfo error: ") 45 | } 46 | gottyURL := "http://127.0.0.1:8080/api/get-terminal" 47 | req, err := http.NewRequest("POST", gottyURL, bytes.NewBuffer(reqBody)) 48 | if err != nil { 49 | return "", errors.Wrap(err, "http.NewRequest error: ") 50 | } 51 | req.Header.Set("Content-Type", "application/json") 52 | client := &http.Client{} 53 | resp, err := client.Do(req) 54 | if err != nil { 55 | return "", errors.Wrap(err, "send req to gotty error: ") 56 | } 57 | defer resp.Body.Close() 58 | 59 | if statusCode := resp.StatusCode; statusCode != 200 { 60 | return "", errors.New("get terminal url from gotty error") 61 | } 62 | 63 | body, err := ioutil.ReadAll(resp.Body) 64 | if err != nil { 65 | return "", errors.Wrap(err, "read resp body error: ") 66 | } 67 | 68 | respBody := new(GetTerminalAPIResponse) 69 | if err := json.Unmarshal(body, respBody); err != nil { 70 | return "", errors.Wrap(err, "Unmarshal resp body error: ") 71 | } 72 | 73 | if !respBody.Success { 74 | return "", errors.New("get terminal url from gotty error: " + respBody.Message) 75 | } 76 | 77 | return "/terminal/?token=" + respBody.Token, nil 78 | } 79 | -------------------------------------------------------------------------------- /src/webterminal/server/handler/handler.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler provides handler. 18 | package handler 19 | 20 | type Handler struct { 21 | 22 | } 23 | 24 | func NewHandler() Handler { 25 | return Handler{} 26 | } -------------------------------------------------------------------------------- /src/webterminal/server/handler/terminal.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 handler 18 | 19 | import ( 20 | "github.com/4paradigm/openaios-platform/src/webterminal/server/apigen" 21 | "github.com/4paradigm/openaios-platform/src/webterminal/server/controller/webterminal" 22 | "github.com/labstack/echo/v4" 23 | "net/http" 24 | ) 25 | 26 | func (h *Handler) GetTerminal(ctx echo.Context, params apigen.GetTerminalParams) error { 27 | userID := ctx.Get("userID").(string) 28 | bearerToken := ctx.Get("bearerToken").(string) 29 | 30 | podName := params.Pod 31 | containerName := params.Container 32 | 33 | terminal := webterminal.Webterminal{ 34 | Namespace: userID, 35 | PodName: podName, 36 | ContainerName: containerName, 37 | BearerToken: bearerToken, 38 | } 39 | 40 | terminalURL, err := webterminal.GetWebterminal(terminal) 41 | if err != nil { 42 | return echo.NewHTTPError(http.StatusInternalServerError).SetInternal(err) 43 | } 44 | 45 | //return ctx.Redirect(http.StatusSeeOther, terminalURL) 46 | return ctx.JSON(http.StatusOK, apigen.WebterminalInfo{Url: &terminalURL}) 47 | } 48 | -------------------------------------------------------------------------------- /src/webterminal/server/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 peizhaoyou 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 main 18 | 19 | import ( 20 | "github.com/labstack/echo-contrib/prometheus" 21 | "github.com/labstack/echo/v4" 22 | "github.com/labstack/echo/v4/middleware" 23 | "github.com/labstack/gommon/log" 24 | "github.com/4paradigm/openaios-platform/src/internal/auth" 25 | "github.com/4paradigm/openaios-platform/src/webterminal/server/apigen" 26 | "github.com/4paradigm/openaios-platform/src/webterminal/server/handler" 27 | ) 28 | 29 | func main() { 30 | e := echo.New() 31 | e.Logger.SetHeader(`${time_rfc3339} ${level} ${short_file}:${line} `) 32 | e.Logger.SetLevel(log.INFO) 33 | 34 | e.Use(middleware.Logger()) 35 | 36 | p := prometheus.NewPrometheus("echo", nil) 37 | p.Use(e) 38 | 39 | //authVerifier, err := auth.NewAuthVerifier() 40 | //if err != nil { 41 | // e.Logger.Fatalf("auth handler initialize failed, err = " + err.Error()) 42 | // return 43 | //} 44 | 45 | h := handler.NewHandler() 46 | 47 | apiGroup := e.Group("/web-terminal") 48 | 49 | if err := auth.InitAuth(); err != nil { 50 | panic(err.Error()) 51 | } 52 | apiGroup.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { 53 | c.Set("bearerToken", key) 54 | idTokenClaim, err := auth.Verify(key) 55 | if err != nil { 56 | c.Logger().Warn("auth verify failed, err = " + err.Error()) 57 | return false, err 58 | } 59 | c.Set("userName", idTokenClaim.PreferredUserName) 60 | c.Set("userID", idTokenClaim.Sub) 61 | return true, nil 62 | })) 63 | 64 | apigen.RegisterHandlers(apiGroup, &h) 65 | 66 | e.Logger.Info(e.Start("0.0.0.0:80")) 67 | 68 | } 69 | --------------------------------------------------------------------------------