diff --git a/frontend/providers/aiproxy/deploy/Kubefile b/frontend/providers/aiproxy/deploy/Kubefile index eeca014ed..3c2317781 100644 --- a/frontend/providers/aiproxy/deploy/Kubefile +++ b/frontend/providers/aiproxy/deploy/Kubefile @@ -1,3 +1,8 @@ FROM scratch -COPY . . -CMD ["bash install.sh"] + +USER 65532:65532 + +COPY charts charts +COPY aiproxy-web-entrypoint.sh aiproxy-web-entrypoint.sh + +CMD ["bash aiproxy-web-entrypoint.sh"] diff --git a/frontend/providers/aiproxy/deploy/aiproxy-web-entrypoint.sh b/frontend/providers/aiproxy/deploy/aiproxy-web-entrypoint.sh new file mode 100755 index 000000000..868fd11a2 --- /dev/null +++ b/frontend/providers/aiproxy/deploy/aiproxy-web-entrypoint.sh @@ -0,0 +1,94 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"aiproxy-web"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"aiproxy-system"} +CHART_PATH=${CHART_PATH:-"./charts/aiproxy"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +get_deploy_env() { + local namespace="$1" + local deployment="$2" + local key="$3" + kubectl get deployment -n "${namespace}" "${deployment}" -o "jsonpath={.spec.template.spec.containers[0].env[?(@.name==\"${key}\")].value}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +NODE_COUNT=$(kubectl get nodes --no-headers 2>/dev/null | wc -l | tr -d " ") +if [ "${NODE_COUNT}" = "1" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set replicas=1" +fi + +JWT_INTERNAL=${JWT_INTERNAL:-"${jwtInternal:-$(get_cm_value sealos-system sealos-config jwtInternal)}"} +ADMIN_KEY=${ADMIN_KEY:-"${adminKey:-$(get_cm_value aiproxy-system aiproxy-env ADMIN_KEY)}"} +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +CURRENCY_SYMBOL=${CURRENCY_SYMBOL:-"${currencySymbol:-$(get_deploy_env dbprovider-frontend dbprovider-frontend CURRENCY_SYMBOL)}"} +SUFFIX=${SUFFIX:-"${suffix:-}"} + +if [ "${CURRENCY_SYMBOL}" = "usd" ] && [ -z "${SUFFIX}" ]; then + SUFFIX="/en/home" +fi + +add_set_string aiproxy.APP_TOKEN_JWT_KEY "${JWT_INTERNAL}" +add_set_string aiproxy.AI_PROXY_BACKEND_KEY "${ADMIN_KEY}" +add_set_string cloudDomain "${SEALOS_CLOUD_DOMAIN}" +add_set_string cloudPort "${SEALOS_CLOUD_PORT}" +add_set_string aiproxy.CURRENCY_SYMBOL "${CURRENCY_SYMBOL}" +add_set_string suffix "${SUFFIX}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap aiproxy-web + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service aiproxy-web + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment aiproxy-web + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress aiproxy-web +fi + +adopt_namespaced_resource app-system apps.app.sealos.io aiproxy + +SERVICE_NAME="aiproxy-web" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/aiproxy/${SERVICE_NAME}-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/aiproxy/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/aiproxy/deploy/charts/aiproxy/aiproxy-web-values.yaml b/frontend/providers/aiproxy/deploy/charts/aiproxy/aiproxy-web-values.yaml new file mode 100644 index 000000000..a19c9b987 --- /dev/null +++ b/frontend/providers/aiproxy/deploy/charts/aiproxy/aiproxy-web-values.yaml @@ -0,0 +1,17 @@ +# Custom values for aiproxy web helm chart. +# This file contains user-customizable configurations. + +replicas: 2 + +resources: + requests: + cpu: 50m + memory: 50Mi + limits: + cpu: 500m + memory: 512Mi + +aiproxy: + CURRENCY_SYMBOL: "" + IS_INVITATION_ACTIVE: "false" + DOC_URL: "https://sealos.run/docs/guides/ai-proxy" diff --git a/frontend/providers/aiproxy/deploy/install.sh b/frontend/providers/aiproxy/deploy/install.sh deleted file mode 100644 index 1c09c39d8..000000000 --- a/frontend/providers/aiproxy/deploy/install.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -timestamp() { - date +"%Y-%m-%d %T" -} -print() { - flag=$(timestamp) - echo -e "\033[1;32m\033[1m INFO [$flag] >> $* \033[0m" -} -warn() { - flag=$(timestamp) - echo -e "\033[33m WARN [$flag] >> $* \033[0m" -} -info() { - flag=$(timestamp) - echo -e "\033[36m INFO [$flag] >> $* \033[0m" -} -#=========================================================================== -HELM_OPTS=${HELM_OPTS:-""} - -NODE_COUNT=$(kubectl get nodes --no-headers | wc -l) -REPLICA_OPTIONS="" -if [ "$NODE_COUNT" -eq 1 ]; then - REPLICA_OPTIONS="--set replicas=1 " - HELM_OPTS="${HELM_OPTS} ${REPLICA_OPTIONS}" -fi - - -varJwtInternal=$(kubectl get configmap sealos-config -n sealos-system -o jsonpath='{.data.jwtInternal}') -adminKey=$(kubectl get configmap aiproxy-env -n aiproxy-system -o jsonpath='{.data.ADMIN_KEY}' ) -SEALOS_CLOUD_DOMAIN=$(kubectl get configmap sealos-config -n sealos-system -o jsonpath='{.data.cloudDomain}') -SEALOS_CLOUD_PORT=$(kubectl get configmap sealos-config -n sealos-system -o jsonpath='{.data.cloudPort}') -CURRENCY_SYMBOL=$(kubectl get deployment -n dbprovider-frontend dbprovider-frontend -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="CURRENCY_SYMBOL")].value}') -if [[ "${CURRENCY_SYMBOL}" == "usd" ]]; then - SUFFIX="/en/home" -fi - -helm upgrade -i aiproxy-web -n aiproxy-system charts/aiproxy ${HELM_OPTS} \ - --set aiproxy.APP_TOKEN_JWT_KEY=${varJwtInternal} --set aiproxy.AI_PROXY_BACKEND_KEY=${adminKey} \ - --set suffix=${SUFFIX} \ - --set cloudDomain=${SEALOS_CLOUD_DOMAIN} --set cloudPort=${SEALOS_CLOUD_PORT} --set aiproxy.CURRENCY_SYMBOL=${CURRENCY_SYMBOL} --create-namespace diff --git a/frontend/providers/cloudserver/deploy/Kubefile b/frontend/providers/cloudserver/deploy/Kubefile index ec1504e7d..5c9ede7b0 100644 --- a/frontend/providers/cloudserver/deploy/Kubefile +++ b/frontend/providers/cloudserver/deploy/Kubefile @@ -3,11 +3,7 @@ FROM scratch USER 65532:65532 COPY registry registry -COPY manifests manifests +COPY charts charts +COPY cloudserver-frontend-entrypoint.sh cloudserver-frontend-entrypoint.sh -ENV cloudDomain="127.0.0.1.nip.io" -ENV cloudPort="" -ENV certSecretName="wildcard-cert" -ENV lafBaseUrl="" - -CMD ["kubectl apply -f manifests"] +CMD ["bash cloudserver-frontend-entrypoint.sh"] diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/Chart.yaml b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/Chart.yaml new file mode 100644 index 000000000..658a6cab9 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: cloudserver-frontend +description: Sealos cloudserver Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/cloudserver-frontend-values.yaml b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/cloudserver-frontend-values.yaml new file mode 100644 index 000000000..0961af480 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/cloudserver-frontend-values.yaml @@ -0,0 +1,16 @@ +# Custom values for cloudserver-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +cloudserverConfig: + lafBaseUrl: "" + jwtSecretApp: "" diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/_helpers.tpl b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..750050caf --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "cloudserver-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "cloudserver-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "cloudserver-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "cloudserver-frontend.labels" -}} +helm.sh/chart: {{ include "cloudserver-frontend.chart" . }} +{{ include "cloudserver-frontend.selectorLabels" . }} +{{ include "cloudserver-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "cloudserver-frontend.selectorLabels" -}} +app: {{ include "cloudserver-frontend.fullname" . }} +{{- end }} + +{{- define "cloudserver-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "cloudserver-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "cloudserver-frontend.scheme" -}} +{{- if eq (toString .Values.cloudserverConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "cloudserver-frontend.port" -}} +{{- $scheme := include "cloudserver-frontend.scheme" . -}} +{{- $port := toString .Values.cloudserverConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.cloudserverConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "cloudserver-frontend.portSuffix" -}} +{{- $port := include "cloudserver-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "cloudserver-frontend.portEnv" -}} +{{- $port := include "cloudserver-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "cloudserver-frontend.cloudOrigin" -}} +{{- include "cloudserver-frontend.scheme" . -}}://{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} +{{- end }} + +{{- define "cloudserver-frontend.wildcardCloudOrigin" -}} +{{- include "cloudserver-frontend.scheme" . -}}://*.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} +{{- end }} + +{{- define "cloudserver-frontend.host" -}} +{{- default (printf "cloudserver.%s" .Values.cloudserverConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "cloudserver-frontend.appUrl" -}} +{{- include "cloudserver-frontend.scheme" . -}}://{{ include "cloudserver-frontend.host" . }}{{ include "cloudserver-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/cloudserver/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/app.yaml similarity index 54% rename from frontend/providers/cloudserver/deploy/manifests/appcr.yaml.tmpl rename to frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/app.yaml index 39b70bc62..2cc086b20 100644 --- a/frontend/providers/cloudserver/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/app.yaml @@ -6,12 +6,14 @@ metadata: spec: data: desc: cloudserver - url: "https://cloudserver.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - icon: "https://cloudserver.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg" + url: {{ include "cloudserver-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "cloudserver-frontend.appUrl" .) | quote }} + i18n: + zh: + name: 云主机 + zh-Hans: + name: 云主机 menuData: name: cloudserver type: iframe displayType: normal - i18n: - zh: - name: 云主机 diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/configmap.yaml b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/configmap.yaml new file mode 100644 index 000000000..79b33e7ab --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "cloudserver-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "cloudserver-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + addr: :3000 diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/deployment.yaml b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/deployment.yaml new file mode 100644 index 000000000..8804cdb86 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/deployment.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "cloudserver-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "cloudserver-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "cloudserver-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + {{- include "cloudserver-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: LAF_BASE_URL + value: {{ .Values.cloudserverConfig.lafBaseUrl | quote }} + - name: JWT_SECRET_APP + value: {{ .Values.cloudserverConfig.jwtSecretApp | quote }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + volumeMounts: + - name: cloudserver-frontend-volume + mountPath: /config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: cloudserver-frontend-volume + configMap: + name: {{ include "cloudserver-frontend.fullname" . }}-config diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/ingress.yaml b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/ingress.yaml new file mode 100644 index 000000000..033902756 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "cloudserver-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "{{ include "cloudserver-frontend.cloudOrigin" . }}, {{ include "cloudserver-frontend.wildcardCloudOrigin" . }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "cloudserver-frontend.cloudOrigin" . }} {{ include "cloudserver-frontend.wildcardCloudOrigin" . }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} {{ .Values.cloudserverConfig.cloudDomain }}{{ include "cloudserver-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "cloudserver-frontend.cloudOrigin" . }} {{ include "cloudserver-frontend.wildcardCloudOrigin" . }}" + X-Xss-Protection "1; mode=block" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "cloudserver-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "cloudserver-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.cloudserverConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "cloudserver-frontend.host" . | quote }} + secretName: {{ .Values.cloudserverConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/service.yaml b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/service.yaml new file mode 100644 index 000000000..b38e8bff8 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "cloudserver-frontend.labels" . | nindent 4 }} + name: {{ include "cloudserver-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "cloudserver-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/values.yaml b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/values.yaml new file mode 100644 index 000000000..679fd4283 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/charts/cloudserver-frontend/values.yaml @@ -0,0 +1,52 @@ +# Default values for cloudserver-frontend helm chart. + +image: ghcr.io/labring/sealos-cloudserver-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "cloudserver-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +cloudserverConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + lafBaseUrl: "" + jwtSecretApp: "" + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +affinity: {} diff --git a/frontend/providers/cloudserver/deploy/cloudserver-frontend-entrypoint.sh b/frontend/providers/cloudserver/deploy/cloudserver-frontend-entrypoint.sh new file mode 100755 index 000000000..f4f820e69 --- /dev/null +++ b/frontend/providers/cloudserver/deploy/cloudserver-frontend-entrypoint.sh @@ -0,0 +1,89 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"cloudserver-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"cloudserver-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/cloudserver-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string cloudserverConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string cloudserverConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string cloudserverConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string cloudserverConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string cloudserverConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" +SEALOS_JWT_INTERNAL=${SEALOS_JWT_INTERNAL:-"${jwtInternal:-$(get_cm_value sealos-system sealos-config jwtInternal)}"} +add_set_string cloudserverConfig.jwtSecretApp "${SEALOS_JWT_INTERNAL}" +add_set_string cloudserverConfig.lafBaseUrl "${lafBaseUrl:-}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap cloudserver-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service cloudserver-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment cloudserver-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress cloudserver-frontend +fi + +adopt_namespaced_resource app-system apps.app.sealos.io cloudserver + + +SERVICE_NAME="cloudserver-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/cloudserver-frontend/cloudserver-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/cloudserver-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/cloudserver/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/cloudserver/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index dd8a5f9bc..000000000 --- a/frontend/providers/cloudserver/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: cloudserver-frontend - name: cloudserver-frontend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: cloudserver-frontend-config - namespace: cloudserver-frontend -data: - config.yaml: |- - addr: :3000 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cloudserver-frontend - namespace: cloudserver-frontend -spec: - replicas: 1 - selector: - matchLabels: - app: cloudserver-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 25% - maxSurge: 25% - template: - metadata: - labels: - app: cloudserver-frontend - spec: - containers: - - name: cloudserver-frontend - env: - - name: LAF_BASE_URL - value: {{ .lafBaseUrl }} - - name: JWT_SECRET_APP - securityContext: - runAsNonRoot: true - runAsUser: 1001 - allowPrivilegeEscalation: false - capabilities: - drop: - - 'ALL' - resources: - limits: - cpu: 1000m - memory: 1024Mi - requests: - cpu: 10m - memory: 128Mi - # do not modify this image, it is used for CI/CD - image: ghcr.io/labring/sealos-cloudserver-frontend:latest - imagePullPolicy: Always - volumeMounts: - - name: cloudserver-frontend-volume - mountPath: /config.yaml - subPath: config.yaml - volumes: - - name: cloudserver-frontend-volume - configMap: - name: cloudserver-frontend-config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: cloudserver-frontend - name: cloudserver-frontend - namespace: cloudserver-frontend -spec: - ports: - - name: http - port: 3000 - protocol: TCP - targetPort: 3000 - selector: - app: cloudserver-frontend diff --git a/frontend/providers/cloudserver/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/cloudserver/deploy/manifests/ingress.yaml.tmpl deleted file mode 100644 index 3136d29ea..000000000 --- a/frontend/providers/cloudserver/deploy/manifests/ingress.yaml.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright © 2023 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/enable-cors: "true" - nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" - nginx.ingress.kubernetes.io/cors-allow-origin: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}, https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - nginx.ingress.kubernetes.io/cors-allow-credentials: "true" - nginx.ingress.kubernetes.io/cors-max-age: "600" - nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - nginx.ingress.kubernetes.io/configuration-snippet: | - more_clear_headers "X-Frame-Options:"; - more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; - more_set_headers "X-Xss-Protection: 1; mode=block"; - higress.io/response-header-control-remove: X-Frame-Options - higress.io/response-header-control-update: | - Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - X-Xss-Protection "1; mode=block" - name: cloudserver-frontend - namespace: cloudserver-frontend -spec: - rules: - - host: cloudserver.{{ .cloudDomain }} - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: cloudserver-frontend - port: - number: 3000 - tls: - - hosts: - - cloudserver.{{ .cloudDomain }} - secretName: {{ .certSecretName }} diff --git a/frontend/providers/costcenter/deploy/Kubefile b/frontend/providers/costcenter/deploy/Kubefile index be67ba1a1..1a1b0f991 100644 --- a/frontend/providers/costcenter/deploy/Kubefile +++ b/frontend/providers/costcenter/deploy/Kubefile @@ -4,5 +4,6 @@ USER 65532:65532 COPY registry registry COPY charts charts +COPY costcenter-frontend-entrypoint.sh costcenter-frontend-entrypoint.sh -CMD ["./costcenter-frontend-entrypoint.sh"] +CMD ["bash costcenter-frontend-entrypoint.sh"] diff --git a/frontend/providers/costcenter/deploy/charts/costcenter-frontend/values.yaml b/frontend/providers/costcenter/deploy/charts/costcenter-frontend/values.yaml index 70cffa406..287880bf5 100644 --- a/frontend/providers/costcenter/deploy/charts/costcenter-frontend/values.yaml +++ b/frontend/providers/costcenter/deploy/charts/costcenter-frontend/values.yaml @@ -9,6 +9,8 @@ fullnameOverride: "costcenter-frontend" podAnnotations: {} podLabels: {} +replicaCount: 1 + podSecurityContext: runAsNonRoot: true runAsUser: 1001 @@ -23,11 +25,46 @@ service: type: ClusterIP port: 3000 +resources: + limits: + cpu: 1000m + memory: 2048Mi + requests: + cpu: 10m + memory: 128Mi + # Costcenter frontend configuration # Note: jwtInternal is auto-configured by entrypoint.sh from sealos-config ConfigMap # For business configurations (transfer, invoice, recharge, etc.), see costcenter-frontend-values.yaml costcenterConfig: jwtInternal: "" # Auto-injected by entrypoint script + transferEnabled: true + currencyType: "shellCoin" + invoice: + enabled: false + feiShuBotURL: "" + aliSms: + endpoint: "" + accessKeyID: "" + accessKeySecret: "" + templateCode: "" + signName: "" + invoiceCompletedTemplateCode: "" + mongo: + uri: "" + recharge: + enabled: false + payMethods: + wechat: + enabled: false + alipay: + enabled: false + stripe: + enabled: false + publicKey: "" + components: + accountService: + url: "http://account-service.account-system.svc:2333" # Affinity for pod anti-affinity affinity: diff --git a/frontend/providers/cronjob/deploy/Kubefile b/frontend/providers/cronjob/deploy/Kubefile index 3dca0d3f3..508e3bea4 100644 --- a/frontend/providers/cronjob/deploy/Kubefile +++ b/frontend/providers/cronjob/deploy/Kubefile @@ -3,13 +3,7 @@ FROM scratch USER 65532:65532 COPY registry registry -COPY manifests manifests +COPY charts charts +COPY cronjob-frontend-entrypoint.sh cronjob-frontend-entrypoint.sh -ENV cloudDomain="127.0.0.1.nip.io" -ENV cloudPort="" -ENV certSecretName="wildcard-cert" - -ENV SUCCESSFUL_JOBS_HISTORY_LIMIT="3" -ENV FAILED_JOBS_HISTORY_LIMIT="3" - -CMD ["kubectl apply -f manifests"] +CMD ["bash cronjob-frontend-entrypoint.sh"] diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/Chart.yaml b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/Chart.yaml new file mode 100644 index 000000000..194ec1505 --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: cronjob-frontend +description: Sealos CronJob Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/cronjob-frontend-values.yaml b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/cronjob-frontend-values.yaml new file mode 100644 index 000000000..c3b6e534e --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/cronjob-frontend-values.yaml @@ -0,0 +1,18 @@ +# Custom values for cronjob-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +cronjobConfig: + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + podCpuMilliCores: 50 + podMemoryMiB: 64 diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/_helpers.tpl b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..b9faa4dc4 --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "cronjob-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "cronjob-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "cronjob-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "cronjob-frontend.labels" -}} +helm.sh/chart: {{ include "cronjob-frontend.chart" . }} +{{ include "cronjob-frontend.selectorLabels" . }} +{{ include "cronjob-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "cronjob-frontend.selectorLabels" -}} +app: {{ include "cronjob-frontend.fullname" . }} +{{- end }} + +{{- define "cronjob-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "cronjob-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "cronjob-frontend.scheme" -}} +{{- if eq (toString .Values.cronjobConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "cronjob-frontend.port" -}} +{{- $scheme := include "cronjob-frontend.scheme" . -}} +{{- $port := toString .Values.cronjobConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.cronjobConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "cronjob-frontend.portSuffix" -}} +{{- $port := include "cronjob-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "cronjob-frontend.portEnv" -}} +{{- $port := include "cronjob-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "cronjob-frontend.cloudOrigin" -}} +{{- include "cronjob-frontend.scheme" . -}}://{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} +{{- end }} + +{{- define "cronjob-frontend.wildcardCloudOrigin" -}} +{{- include "cronjob-frontend.scheme" . -}}://*.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} +{{- end }} + +{{- define "cronjob-frontend.host" -}} +{{- default (printf "cronjob.%s" .Values.cronjobConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "cronjob-frontend.appUrl" -}} +{{- include "cronjob-frontend.scheme" . -}}://{{ include "cronjob-frontend.host" . }}{{ include "cronjob-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/cronjob/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/app.yaml similarity index 58% rename from frontend/providers/cronjob/deploy/manifests/appcr.yaml.tmpl rename to frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/app.yaml index f47dd3cc1..4571f21c4 100644 --- a/frontend/providers/cronjob/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/app.yaml @@ -6,14 +6,14 @@ metadata: spec: data: desc: CronJob - url: "https://cronjob.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - icon: "https://cronjob.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg" - menuData: - name: CronJob - type: iframe - displayType: normal + url: {{ include "cronjob-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "cronjob-frontend.appUrl" .) | quote }} i18n: zh: name: 定时任务 zh-Hans: name: 定时任务 + menuData: + name: CronJob + type: iframe + displayType: normal diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/configmap.yaml b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/configmap.yaml new file mode 100644 index 000000000..351d93a74 --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/configmap.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "cronjob-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "cronjob-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + cloud: + domain: {{ .Values.cronjobConfig.cloudDomain | quote }} + desktopDomain: {{ .Values.cronjobConfig.cloudDomain | quote }} + cronjob: + components: + applaunchpad: + url: {{ printf "https://applaunchpad.%s%s" .Values.cronjobConfig.cloudDomain (include "cronjob-frontend.portSuffix" .) | quote }} + podResources: + cpuMilliCores: {{ .Values.cronjobConfig.podCpuMilliCores }} + memoryMiB: {{ .Values.cronjobConfig.podMemoryMiB }} + jobHistory: + successfulLimit: {{ .Values.cronjobConfig.successfulJobsHistoryLimit }} + failedLimit: {{ .Values.cronjobConfig.failedJobsHistoryLimit }} diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/deployment.yaml b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/deployment.yaml new file mode 100644 index 000000000..adab4ceb2 --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/deployment.yaml @@ -0,0 +1,67 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "cronjob-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "cronjob-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "cronjob-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + {{- include "cronjob-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: SEALOS_DOMAIN + value: {{ .Values.cronjobConfig.cloudDomain | quote }} + - name: APPLAUNCHPAD_URL + value: {{ printf "applaunchpad.%s" .Values.cronjobConfig.cloudDomain | quote }} + - name: SUCCESSFUL_JOBS_HISTORY_LIMIT + value: {{ .Values.cronjobConfig.successfulJobsHistoryLimit | quote }} + - name: FAILED_JOBS_HISTORY_LIMIT + value: {{ .Values.cronjobConfig.failedJobsHistoryLimit | quote }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + volumeMounts: + - name: cronjob-frontend-volume + mountPath: /app/data/config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: cronjob-frontend-volume + configMap: + name: {{ include "cronjob-frontend.fullname" . }}-config diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/ingress.yaml b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/ingress.yaml new file mode 100644 index 000000000..6a2d2a896 --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "cronjob-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "{{ include "cronjob-frontend.cloudOrigin" . }}, {{ include "cronjob-frontend.wildcardCloudOrigin" . }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "cronjob-frontend.cloudOrigin" . }} {{ include "cronjob-frontend.wildcardCloudOrigin" . }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} {{ .Values.cronjobConfig.cloudDomain }}{{ include "cronjob-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "cronjob-frontend.cloudOrigin" . }} {{ include "cronjob-frontend.wildcardCloudOrigin" . }}" + X-Xss-Protection "1; mode=block" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "cronjob-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "cronjob-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.cronjobConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "cronjob-frontend.host" . | quote }} + secretName: {{ .Values.cronjobConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/service.yaml b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/service.yaml new file mode 100644 index 000000000..7357c6c59 --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "cronjob-frontend.labels" . | nindent 4 }} + name: {{ include "cronjob-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "cronjob-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/cronjob/deploy/charts/cronjob-frontend/values.yaml b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/values.yaml new file mode 100644 index 000000000..9d4fb56c7 --- /dev/null +++ b/frontend/providers/cronjob/deploy/charts/cronjob-frontend/values.yaml @@ -0,0 +1,65 @@ +# Default values for cronjob-frontend helm chart. + +image: ghcr.io/labring/sealos-cronjob-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "cronjob-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +cronjobConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + podCpuMilliCores: 50 + podMemoryMiB: 64 + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - cronjob-frontend + topologyKey: "kubernetes.io/hostname" diff --git a/frontend/providers/cronjob/deploy/cronjob-frontend-entrypoint.sh b/frontend/providers/cronjob/deploy/cronjob-frontend-entrypoint.sh new file mode 100755 index 000000000..51dc4f317 --- /dev/null +++ b/frontend/providers/cronjob/deploy/cronjob-frontend-entrypoint.sh @@ -0,0 +1,90 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"cronjob-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"cronjob-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/cronjob-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string cronjobConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string cronjobConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string cronjobConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string cronjobConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string cronjobConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" +add_set_string cronjobConfig.successfulJobsHistoryLimit "${successfulJobsHistoryLimit:-}" +add_set_string cronjobConfig.failedJobsHistoryLimit "${failedJobsHistoryLimit:-}" +add_set_string cronjobConfig.podCpuMilliCores "${podCpuMilliCores:-}" +add_set_string cronjobConfig.podMemoryMiB "${podMemoryMiB:-}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap cronjob-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service cronjob-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment cronjob-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress cronjob-frontend +fi + +adopt_namespaced_resource app-system apps.app.sealos.io cronjob + + +SERVICE_NAME="cronjob-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/cronjob-frontend/cronjob-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/cronjob-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/cronjob/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/cronjob/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index 6b1cb420e..000000000 --- a/frontend/providers/cronjob/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,100 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: cronjob-frontend - name: cronjob-frontend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: cronjob-frontend-config - namespace: cronjob-frontend -data: - config.yaml: |- - addr: :3000 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cronjob-frontend - namespace: cronjob-frontend -spec: - replicas: 1 - selector: - matchLabels: - app: cronjob-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 25% - maxSurge: 25% - template: - metadata: - labels: - app: cronjob-frontend - spec: - containers: - - name: cronjob-frontend - env: - - name: SEALOS_DOMAIN - value: {{ .cloudDomain }} - - name: APPLAUNCHPAD_URL - value: applaunchpad.{{ .cloudDomain }} - - name: SUCCESSFUL_JOBS_HISTORY_LIMIT - value: "{{ .SUCCESSFUL_JOBS_HISTORY_LIMIT }}" - - name: FAILED_JOBS_HISTORY_LIMIT - value: "{{ .FAILED_JOBS_HISTORY_LIMIT }}" - securityContext: - runAsNonRoot: true - runAsUser: 1001 - allowPrivilegeEscalation: false - capabilities: - drop: - - 'ALL' - resources: - limits: - cpu: 1000m - memory: 1024Mi - requests: - cpu: 10m - memory: 128Mi - # do not modify this image, it is used for CI/CD - image: ghcr.io/labring/sealos-cronjob-frontend:latest - imagePullPolicy: Always - volumeMounts: - - name: cronjob-frontend-volume - mountPath: /config.yaml - subPath: config.yaml - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "app" - operator: In - values: - - cronjob-frontend - topologyKey: "kubernetes.io/hostname" - volumes: - - name: cronjob-frontend-volume - configMap: - name: cronjob-frontend-config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: cronjob-frontend - name: cronjob-frontend - namespace: cronjob-frontend -spec: - ports: - - name: http - port: 3000 - protocol: TCP - targetPort: 3000 - selector: - app: cronjob-frontend diff --git a/frontend/providers/cronjob/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/cronjob/deploy/manifests/ingress.yaml.tmpl deleted file mode 100644 index 89fc415cd..000000000 --- a/frontend/providers/cronjob/deploy/manifests/ingress.yaml.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright © 2023 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/enable-cors: "true" - nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" - nginx.ingress.kubernetes.io/cors-allow-origin: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}, https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - nginx.ingress.kubernetes.io/cors-allow-credentials: "true" - nginx.ingress.kubernetes.io/cors-max-age: "600" - nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - nginx.ingress.kubernetes.io/configuration-snippet: | - more_clear_headers "X-Frame-Options:"; - more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; - more_set_headers "X-Xss-Protection: 1; mode=block"; - higress.io/response-header-control-remove: X-Frame-Options - higress.io/response-header-control-update: | - Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - X-Xss-Protection "1; mode=block" - name: cronjob-frontend - namespace: cronjob-frontend -spec: - rules: - - host: cronjob.{{ .cloudDomain }} - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: cronjob-frontend - port: - number: 3000 - tls: - - hosts: - - cronjob.{{ .cloudDomain }} - secretName: {{ .certSecretName }} diff --git a/frontend/providers/dbprovider/deploy/Kubefile b/frontend/providers/dbprovider/deploy/Kubefile index 36c4c08d4..e19bd9f5a 100644 --- a/frontend/providers/dbprovider/deploy/Kubefile +++ b/frontend/providers/dbprovider/deploy/Kubefile @@ -3,20 +3,7 @@ FROM scratch USER 65532:65532 COPY registry registry -COPY manifests manifests +COPY charts charts +COPY dbprovider-frontend-entrypoint.sh dbprovider-frontend-entrypoint.sh -ENV cloudDomain="127.0.0.1.nip.io" -ENV cloudPort="" -ENV certSecretName="wildcard-cert" -ENV monitorUrl="http://database-monitor.sealos.svc.cluster.local:9090" -ENV minioUrl="" -ENV minioAccessKey="" -ENV minioSecretKey="" -ENV minioPort="" -ENV migrateFileImage="" -ENV minioBucketName="" -ENV guideEnabled="true" -ENV billingUrl="http://account-service.account-system.svc:2333" -ENV vlogsBaseUrl="http://service-vlogs.sealos.svc.cluster.local:8428" - -CMD ["kubectl apply -f manifests"] +CMD ["bash dbprovider-frontend-entrypoint.sh"] diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/Chart.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/Chart.yaml new file mode 100644 index 000000000..95a32b1a3 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: dbprovider-frontend +description: Sealos Database Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/dbprovider-frontend-values.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/dbprovider-frontend-values.yaml new file mode 100644 index 000000000..117f5f211 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/dbprovider-frontend-values.yaml @@ -0,0 +1,47 @@ +# Custom values for dbprovider-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 10m + memory: 128Mi + +dbproviderConfig: + monitorUrl: "http://database-monitor.sealos.svc.cluster.local:9090" + minioUrl: "" + minioAccessKey: "" + minioSecretKey: "" + minioPort: 9000 + minioUseSSL: false + minioBucketName: "file-migration" + migrateFileFetchFileImage: "quay.io/minio/mc:latest" + migrateFileImportDataImage: "sealos.hub:5000/db-import-client:latest" + guideEnabled: true + billingUrl: "http://account-service.account-system.svc:2333" + billingSecret: "" + alertingUrl: "http://database-alert-service.sealos.svc.cluster.local:8000" + alertingSecret: "" + vlogsBaseUrl: "http://service-vlogs.sealos.svc.cluster.local:8428" + migrationJobCPURequirement: 2000 + migrationJobMemoryRequirement: 4096 + dumpImportJobCPURequirement: 2000 + dumpImportJobMemoryRequirement: 4096 + backupJobCPURequirement: 300 + backupJobMemoryRequirement: 256 + backupEnabled: false + showDocument: true + managedDbEnabled: false + fileImportEnabled: false + currencySymbol: "shellCoin" + forcedStorageClassName: "" + storageMaxSize: 300 + customScripts: [] + eventAnalysisEnabled: false + eventAnalysisUrl: "https://fastgpt.run/api/openapi" + eventAnalysisModelId: "6455c433f437e55c638e630c" + eventAnalysisFastGPTKey: "" diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/_helpers.tpl b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..fa4a712b6 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "dbprovider-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "dbprovider-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "dbprovider-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "dbprovider-frontend.labels" -}} +helm.sh/chart: {{ include "dbprovider-frontend.chart" . }} +{{ include "dbprovider-frontend.selectorLabels" . }} +{{ include "dbprovider-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "dbprovider-frontend.selectorLabels" -}} +app: {{ include "dbprovider-frontend.fullname" . }} +{{- end }} + +{{- define "dbprovider-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "dbprovider-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "dbprovider-frontend.scheme" -}} +{{- if eq (toString .Values.dbproviderConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "dbprovider-frontend.port" -}} +{{- $scheme := include "dbprovider-frontend.scheme" . -}} +{{- $port := toString .Values.dbproviderConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.dbproviderConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "dbprovider-frontend.portSuffix" -}} +{{- $port := include "dbprovider-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "dbprovider-frontend.portEnv" -}} +{{- $port := include "dbprovider-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "dbprovider-frontend.cloudOrigin" -}} +{{- include "dbprovider-frontend.scheme" . -}}://{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} +{{- end }} + +{{- define "dbprovider-frontend.wildcardCloudOrigin" -}} +{{- include "dbprovider-frontend.scheme" . -}}://*.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} +{{- end }} + +{{- define "dbprovider-frontend.host" -}} +{{- default (printf "dbprovider.%s" .Values.dbproviderConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "dbprovider-frontend.appUrl" -}} +{{- include "dbprovider-frontend.scheme" . -}}://{{ include "dbprovider-frontend.host" . }}{{ include "dbprovider-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/dbprovider/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/app.yaml similarity index 57% rename from frontend/providers/dbprovider/deploy/manifests/appcr.yaml.tmpl rename to frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/app.yaml index ddde5f372..a6242660b 100644 --- a/frontend/providers/dbprovider/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/app.yaml @@ -6,8 +6,8 @@ metadata: spec: data: desc: Database - url: "https://dbprovider.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - icon: "https://dbprovider.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg" + url: {{ include "dbprovider-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "dbprovider-frontend.appUrl" .) | quote }} i18n: zh: name: 数据库 diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/configmap.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/configmap.yaml new file mode 100644 index 000000000..5dc7eb6e9 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/configmap.yaml @@ -0,0 +1,58 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "dbprovider-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "dbprovider-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + cloud: + domain: {{ .Values.dbproviderConfig.cloudDomain | quote }} + desktopDomain: {{ .Values.dbproviderConfig.cloudDomain | quote }} + dbprovider: + features: + guide: {{ .Values.dbproviderConfig.guideEnabled }} + showDocument: {{ .Values.dbproviderConfig.showDocument }} + fileImport: {{ .Values.dbproviderConfig.fileImportEnabled }} + ui: + currencySymbol: {{ .Values.dbproviderConfig.currencySymbol | quote }} + customScripts: + {{- toYaml .Values.dbproviderConfig.customScripts | nindent 10 }} + storage: + forcedClassName: {{ if .Values.dbproviderConfig.forcedStorageClassName }}{{ .Values.dbproviderConfig.forcedStorageClassName | quote }}{{ else }}null{{ end }} + maxSize: {{ .Values.dbproviderConfig.storageMaxSize }} + components: + monitoring: + url: {{ .Values.dbproviderConfig.monitorUrl | quote }} + alerting: + url: {{ .Values.dbproviderConfig.alertingUrl | quote }} + secret: {{ .Values.dbproviderConfig.alertingSecret | quote }} + billing: + url: {{ .Values.dbproviderConfig.billingUrl | quote }} + secret: {{ .Values.dbproviderConfig.billingSecret | quote }} + logging: + url: {{ .Values.dbproviderConfig.vlogsBaseUrl | quote }} + storage: + url: {{ .Values.dbproviderConfig.minioUrl | quote }} + port: {{ .Values.dbproviderConfig.minioPort }} + useSSL: {{ .Values.dbproviderConfig.minioUseSSL }} + bucketName: {{ .Values.dbproviderConfig.minioBucketName | quote }} + accessKey: {{ .Values.dbproviderConfig.minioAccessKey | quote }} + secretKey: {{ .Values.dbproviderConfig.minioSecretKey | quote }} + eventAnalysis: + enabled: {{ .Values.dbproviderConfig.eventAnalysisEnabled }} + url: {{ .Values.dbproviderConfig.eventAnalysisUrl | quote }} + modelId: {{ .Values.dbproviderConfig.eventAnalysisModelId | quote }} + fastGPTKey: {{ .Values.dbproviderConfig.eventAnalysisFastGPTKey | quote }} + migration: + fetchFileImage: {{ .Values.dbproviderConfig.migrateFileFetchFileImage | quote }} + importDataImage: {{ .Values.dbproviderConfig.migrateFileImportDataImage | quote }} + jobCpuMillicores: {{ .Values.dbproviderConfig.migrationJobCPURequirement }} + jobMemoryMiB: {{ .Values.dbproviderConfig.migrationJobMemoryRequirement }} + dumpImportCpuMillicores: {{ .Values.dbproviderConfig.dumpImportJobCPURequirement }} + dumpImportMemoryMiB: {{ .Values.dbproviderConfig.dumpImportJobMemoryRequirement }} + backup: + enabled: {{ .Values.dbproviderConfig.backupEnabled }} + jobCpuMillicores: {{ .Values.dbproviderConfig.backupJobCPURequirement }} + jobMemoryMiB: {{ .Values.dbproviderConfig.backupJobMemoryRequirement }} diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/deployment.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/deployment.yaml new file mode 100644 index 000000000..7cd313974 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/deployment.yaml @@ -0,0 +1,108 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "dbprovider-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "dbprovider-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "dbprovider-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + {{- include "dbprovider-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + serviceAccountName: cluster-version-reader + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: SEALOS_DOMAIN + value: {{ .Values.dbproviderConfig.cloudDomain | quote }} + - name: DESKTOP_DOMAIN + value: {{ .Values.dbproviderConfig.cloudDomain | quote }} + - name: SEALOS_PORT + value: {{ include "dbprovider-frontend.portEnv" . | quote }} + - name: MONITOR_URL + value: {{ .Values.dbproviderConfig.monitorUrl | quote }} + - name: MINIO_URL + value: {{ .Values.dbproviderConfig.minioUrl | quote }} + - name: MINIO_ACCESS_KEY + value: {{ .Values.dbproviderConfig.minioAccessKey | quote }} + - name: MINIO_SECRET_KEY + value: {{ .Values.dbproviderConfig.minioSecretKey | quote }} + - name: MIGRATE_FILE_FETCH_FILE_IMAGE + value: {{ .Values.dbproviderConfig.migrateFileFetchFileImage | quote }} + - name: MIGRATE_FILE_IMPORT_DATA_IMAGE + value: {{ .Values.dbproviderConfig.migrateFileImportDataImage | quote }} + - name: MINIO_PORT + value: {{ .Values.dbproviderConfig.minioPort | quote }} + - name: MINIO_BUCKET_NAME + value: {{ .Values.dbproviderConfig.minioBucketName | quote }} + - name: BACKUP_ENABLED + value: {{ .Values.dbproviderConfig.backupEnabled | quote }} + - name: SHOW_DOCUMENT + value: {{ .Values.dbproviderConfig.showDocument | quote }} + - name: GUIDE_ENABLED + value: {{ .Values.dbproviderConfig.guideEnabled | quote }} + - name: BILLING_URL + value: {{ .Values.dbproviderConfig.billingUrl | quote }} + - name: MANAGED_DB_ENABLED + value: {{ .Values.dbproviderConfig.managedDbEnabled | quote }} + - name: VLOGS_BASE_URL + value: {{ .Values.dbproviderConfig.vlogsBaseUrl | quote }} + - name: MIGRATION_JOB_CPU_REQUIREMENT + value: {{ .Values.dbproviderConfig.migrationJobCPURequirement | quote }} + - name: MIGRATION_JOB_MEMORY_REQUIREMENT + value: {{ .Values.dbproviderConfig.migrationJobMemoryRequirement | quote }} + - name: DUMPIMPORT_JOB_CPU_REQUIREMENT + value: {{ .Values.dbproviderConfig.dumpImportJobCPURequirement | quote }} + - name: DUMPIMPORT_JOB_MEMORY_REQUIREMENT + value: {{ .Values.dbproviderConfig.dumpImportJobMemoryRequirement | quote }} + - name: BACKUP_JOB_CPU_REQUIREMENT + value: {{ .Values.dbproviderConfig.backupJobCPURequirement | quote }} + - name: BACKUP_JOB_MEMORY_REQUIREMENT + value: {{ .Values.dbproviderConfig.backupJobMemoryRequirement | quote }} + - name: CURRENCY_SYMBOL + value: {{ .Values.dbproviderConfig.currencySymbol | quote }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + volumeMounts: + - name: dbprovider-frontend-volume + mountPath: /app/data/config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: dbprovider-frontend-volume + configMap: + name: {{ include "dbprovider-frontend.fullname" . }}-config diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/ingress.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/ingress.yaml new file mode 100644 index 000000000..d7bfa05ce --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "dbprovider-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "{{ include "dbprovider-frontend.cloudOrigin" . }}, {{ include "dbprovider-frontend.wildcardCloudOrigin" . }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "dbprovider-frontend.cloudOrigin" . }} {{ include "dbprovider-frontend.wildcardCloudOrigin" . }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} {{ .Values.dbproviderConfig.cloudDomain }}{{ include "dbprovider-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "dbprovider-frontend.cloudOrigin" . }} {{ include "dbprovider-frontend.wildcardCloudOrigin" . }}" + X-Xss-Protection "1; mode=block" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "dbprovider-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "dbprovider-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.dbproviderConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "dbprovider-frontend.host" . | quote }} + secretName: {{ .Values.dbproviderConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/rbac.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/rbac.yaml new file mode 100644 index 000000000..69856a0c5 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/rbac.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cluster-version-reader + namespace: {{ .Release.Namespace }} + labels: + {{- include "dbprovider-frontend.labels" . | nindent 4 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cluster-version-reader +rules: + - apiGroups: ["extensions.kubeblocks.io"] + resources: ["addons"] + verbs: ["get", "watch", "list"] + - apiGroups: ["apps.kubeblocks.io"] + resources: ["clusterversions"] + verbs: ["get", "watch", "list"] + - apiGroups: ["dataprotection.kubeblocks.io"] + resources: ["backuprepos"] + verbs: ["get", "watch", "list"] + - apiGroups: ["apps.kubeblocks.io"] + resources: ["componentversions"] + verbs: ["get", "watch", "list"] + - apiGroups: ["app.sealos.io"] + resources: ["apps"] + resourceNames: ["dataflow"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cluster-version-reader-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-version-reader +subjects: + - kind: ServiceAccount + name: cluster-version-reader + namespace: {{ .Release.Namespace }} diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/service.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/service.yaml new file mode 100644 index 000000000..2be0ae985 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "dbprovider-frontend.labels" . | nindent 4 }} + name: {{ include "dbprovider-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "dbprovider-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/values.yaml b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/values.yaml new file mode 100644 index 000000000..cd7676b7e --- /dev/null +++ b/frontend/providers/dbprovider/deploy/charts/dbprovider-frontend/values.yaml @@ -0,0 +1,94 @@ +# Default values for dbprovider-frontend helm chart. + +image: ghcr.io/labring/sealos-dbprovider-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "dbprovider-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +dbproviderConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + monitorUrl: "http://database-monitor.sealos.svc.cluster.local:9090" + minioUrl: "" + minioAccessKey: "" + minioSecretKey: "" + minioPort: 9000 + minioUseSSL: false + minioBucketName: "file-migration" + migrateFileFetchFileImage: "quay.io/minio/mc:latest" + migrateFileImportDataImage: "sealos.hub:5000/db-import-client:latest" + guideEnabled: true + billingUrl: "http://account-service.account-system.svc:2333" + billingSecret: "" + alertingUrl: "http://database-alert-service.sealos.svc.cluster.local:8000" + alertingSecret: "" + vlogsBaseUrl: "http://service-vlogs.sealos.svc.cluster.local:8428" + migrationJobCPURequirement: 2000 + migrationJobMemoryRequirement: 4096 + dumpImportJobCPURequirement: 2000 + dumpImportJobMemoryRequirement: 4096 + backupJobCPURequirement: 300 + backupJobMemoryRequirement: 256 + backupEnabled: false + showDocument: true + managedDbEnabled: false + fileImportEnabled: false + currencySymbol: "shellCoin" + forcedStorageClassName: "" + storageMaxSize: 300 + customScripts: [] + eventAnalysisEnabled: false + eventAnalysisUrl: "https://fastgpt.run/api/openapi" + eventAnalysisModelId: "6455c433f437e55c638e630c" + eventAnalysisFastGPTKey: "" + +resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 10m + memory: 128Mi + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - dbprovider-frontend + topologyKey: "kubernetes.io/hostname" diff --git a/frontend/providers/dbprovider/deploy/dbprovider-frontend-entrypoint.sh b/frontend/providers/dbprovider/deploy/dbprovider-frontend-entrypoint.sh new file mode 100755 index 000000000..822e2e247 --- /dev/null +++ b/frontend/providers/dbprovider/deploy/dbprovider-frontend-entrypoint.sh @@ -0,0 +1,121 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"dbprovider-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"dbprovider-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/dbprovider-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string dbproviderConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string dbproviderConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string dbproviderConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string dbproviderConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string dbproviderConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" +add_set_string dbproviderConfig.monitorUrl "${monitorUrl:-}" +add_set_string dbproviderConfig.minioUrl "${minioUrl:-}" +add_set_string dbproviderConfig.minioAccessKey "${minioAccessKey:-}" +add_set_string dbproviderConfig.minioSecretKey "${minioSecretKey:-}" +add_set_string dbproviderConfig.minioPort "${minioPort:-}" +add_set_string dbproviderConfig.minioUseSSL "${minioUseSSL:-}" +add_set_string dbproviderConfig.minioBucketName "${minioBucketName:-}" +add_set_string dbproviderConfig.migrateFileFetchFileImage "${migrateFileFetchFileImage:-}" +add_set_string dbproviderConfig.migrateFileImportDataImage "${migrateFileImportDataImage:-}" +add_set_string dbproviderConfig.guideEnabled "${guideEnabled:-}" +add_set_string dbproviderConfig.billingUrl "${billingUrl:-}" +add_set_string dbproviderConfig.billingSecret "${billingSecret:-}" +add_set_string dbproviderConfig.alertingUrl "${alertingUrl:-}" +add_set_string dbproviderConfig.alertingSecret "${alertingSecret:-}" +add_set_string dbproviderConfig.vlogsBaseUrl "${vlogsBaseUrl:-}" +add_set_string dbproviderConfig.migrationJobCPURequirement "${migrationJobCPURequirement:-}" +add_set_string dbproviderConfig.migrationJobMemoryRequirement "${migrationJobMemoryRequirement:-}" +add_set_string dbproviderConfig.dumpImportJobCPURequirement "${dumpImportJobCPURequirement:-}" +add_set_string dbproviderConfig.dumpImportJobMemoryRequirement "${dumpImportJobMemoryRequirement:-}" +add_set_string dbproviderConfig.backupJobCPURequirement "${backupJobCPURequirement:-}" +add_set_string dbproviderConfig.backupJobMemoryRequirement "${backupJobMemoryRequirement:-}" +add_set_string dbproviderConfig.backupEnabled "${backupEnabled:-}" +add_set_string dbproviderConfig.showDocument "${showDocument:-}" +add_set_string dbproviderConfig.managedDbEnabled "${managedDbEnabled:-}" +add_set_string dbproviderConfig.fileImportEnabled "${fileImportEnabled:-}" +add_set_string dbproviderConfig.currencySymbol "${currencySymbol:-}" +add_set_string dbproviderConfig.forcedStorageClassName "${forcedStorageClassName:-}" +add_set_string dbproviderConfig.storageMaxSize "${storageMaxSize:-}" +add_set_string dbproviderConfig.customScripts "${customScripts:-}" +add_set_string dbproviderConfig.eventAnalysisEnabled "${eventAnalysisEnabled:-}" +add_set_string dbproviderConfig.eventAnalysisUrl "${eventAnalysisUrl:-}" +add_set_string dbproviderConfig.eventAnalysisModelId "${eventAnalysisModelId:-}" +add_set_string dbproviderConfig.eventAnalysisFastGPTKey "${eventAnalysisFastGPTKey:-}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" serviceaccount cluster-version-reader + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap dbprovider-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service dbprovider-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment dbprovider-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress dbprovider-frontend +fi + +adopt_namespaced_resource app-system apps.app.sealos.io dbprovider +adopt_cluster_resource clusterrole cluster-version-reader +adopt_cluster_resource clusterrolebinding cluster-version-reader-rolebinding + +SERVICE_NAME="dbprovider-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/dbprovider-frontend/dbprovider-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/dbprovider-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/dbprovider/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/dbprovider/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index 8b5931d57..000000000 --- a/frontend/providers/dbprovider/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,139 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: dbprovider-frontend - name: dbprovider-frontend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: dbprovider-frontend-config - namespace: dbprovider-frontend -data: - config.yaml: |- - addr: :3000 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: dbprovider-frontend - namespace: dbprovider-frontend -spec: - replicas: 1 - selector: - matchLabels: - app: dbprovider-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 25% - maxSurge: 25% - template: - metadata: - labels: - app: dbprovider-frontend - spec: - serviceAccountName: cluster-version-reader - containers: - - name: dbprovider-frontend - env: - - name: SEALOS_DOMAIN - value: {{ .cloudDomain }} - - name: DESKTOP_DOMAIN - value: {{ .cloudDomain }} - - name: SEALOS_PORT - value: "{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - - name: MONITOR_URL - value: {{ .monitorUrl }} - - name: MINIO_URL - value: {{ .minioUrl }} - - name: MINIO_ACCESS_KEY - value: {{ .minioAccessKey }} - - name: MINIO_SECRET_KEY - value: {{ .minioSecretKey }} - - name: MIGRATE_FILE_FETCH_FILE_IMAGE - value: {{ .migrateFileFetchFileImage }} - - name: MIGRATE_FILE_IMPORT_DATA_IMAGE - value: {{ .migrateFileImportDataImage }} - - name: MINIO_PORT - value: '{{ .minioPort }}' - - name: MINIO_BUCKET_NAME - value: {{ .minioBucketName }} - - name: BACKUP_ENABLED - value: "false" - - name: SHOW_DOCUMENT - value: "true" - - name: GUIDE_ENABLED - value: "{{ .guideEnabled }}" - - name: BILLING_URL - value: {{ .billingUrl }} - - name: MANAGED_DB_ENABLED - value: "false" - - name: VLOGS_BASE_URL - value: {{ .vlogsBaseUrl }} - - name: MIGRATION_JOB_CPU_REQUIREMENT - value: {{ .migrationJobCPURequirement }} - - name: MIGRATION_JOB_MEMORY_REQUIREMENT - value: {{ .migrationJobMemoryRequirement }} - - name: DUMPIMPORT_JOB_CPU_REQUIREMENT - value: {{ .dumpImportJobCPURequirement }} - - name: DUMPIMPORT_JOB_MEMORY_REQUIREMENT - value: {{ .dumpImportJobMemoryRequirement }} - - name: BACKUP_JOB_CPU_REQUIREMENT - value: {{ .backupJobCPURequirement }} - - name: BACKUP_JOB_MEMORY_REQUIREMENT - value: {{ .backupJobMemoryRequirement }} - securityContext: - runAsNonRoot: true - runAsUser: 1001 - allowPrivilegeEscalation: false - capabilities: - drop: - - 'ALL' - resources: - limits: - cpu: 2000m - memory: 2048Mi - requests: - cpu: 10m - memory: 128Mi - # do not modify this image, it is used for CI/CD - image: ghcr.io/labring/sealos-dbprovider-frontend:latest - imagePullPolicy: Always - volumeMounts: - - name: dbprovider-frontend-volume - mountPath: /config.yaml - subPath: config.yaml - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "app" - operator: In - values: - - dbprovider-frontend - topologyKey: "kubernetes.io/hostname" - volumes: - - name: dbprovider-frontend-volume - configMap: - name: dbprovider-frontend-config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: dbprovider-frontend - name: dbprovider-frontend - namespace: dbprovider-frontend -spec: - ports: - - name: http - port: 3000 - protocol: TCP - targetPort: 3000 - selector: - app: dbprovider-frontend diff --git a/frontend/providers/dbprovider/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/dbprovider/deploy/manifests/ingress.yaml.tmpl deleted file mode 100644 index efe215feb..000000000 --- a/frontend/providers/dbprovider/deploy/manifests/ingress.yaml.tmpl +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/configuration-snippet: | - more_clear_headers "X-Frame-Options:"; - more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com; frame-src 'self' {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; - more_set_headers "X-Xss-Protection: 1; mode=block"; - higress.io/response-header-control-remove: X-Frame-Options - higress.io/response-header-control-update: | - Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com; frame-src 'self' {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - X-Xss-Protection "1; mode=block" - name: dbprovider-frontend - namespace: dbprovider-frontend -spec: - rules: - - host: dbprovider.{{ .cloudDomain }} - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: dbprovider-frontend - port: - number: 3000 - tls: - - hosts: - - dbprovider.{{ .cloudDomain }} - secretName: {{ .certSecretName }} diff --git a/frontend/providers/dbprovider/deploy/manifests/rbac.yaml b/frontend/providers/dbprovider/deploy/manifests/rbac.yaml deleted file mode 100644 index e3136050a..000000000 --- a/frontend/providers/dbprovider/deploy/manifests/rbac.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cluster-version-reader - namespace: dbprovider-frontend ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: cluster-version-reader -rules: - - apiGroups: [ 'extensions.kubeblocks.io' ] - resources: [ 'addons' ] - verbs: [ 'get', 'watch', 'list' ] - - apiGroups: ['apps.kubeblocks.io'] - resources: ['clusterversions'] - verbs: ['get', 'watch', 'list'] - - apiGroups: ['dataprotection.kubeblocks.io'] - resources: ['backuprepos'] - verbs: ['get', 'watch', 'list'] - - apiGroups: ['apps.kubeblocks.io'] - resources: ['componentversions'] - verbs: ['get', 'watch', 'list'] - - apiGroups: ['app.sealos.io'] - resources: ['apps'] - resourceNames: ['dataflow'] - verbs: ['get'] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: cluster-version-reader-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-version-reader -subjects: - - kind: ServiceAccount - name: cluster-version-reader - namespace: dbprovider-frontend diff --git a/frontend/providers/devbox/deploy/Kubefile b/frontend/providers/devbox/deploy/Kubefile index b5729882e..d03496743 100644 --- a/frontend/providers/devbox/deploy/Kubefile +++ b/frontend/providers/devbox/deploy/Kubefile @@ -3,11 +3,7 @@ FROM scratch USER 65532:65532 COPY registry registry -COPY manifests manifests +COPY charts charts +COPY devbox-frontend-entrypoint.sh devbox-frontend-entrypoint.sh -ENV cloudDomain="127.0.0.1.nip.io" -ENV cloudPort="" -ENV certSecretName="wildcard-cert" -ENV registryAddr="sealos.hub:5000" - -CMD ["kubectl apply -f manifests"] +CMD ["bash devbox-frontend-entrypoint.sh"] diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/Chart.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/Chart.yaml new file mode 100644 index 000000000..a396f671a --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: devbox-frontend +description: Sealos DevBox Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/devbox-frontend-values.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/devbox-frontend-values.yaml new file mode 100644 index 000000000..4b4e1b48d --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/devbox-frontend-values.yaml @@ -0,0 +1,41 @@ +# Custom values for devbox-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 100m + memory: 128Mi + +devboxConfig: + databaseUrl: "" + databaseProvider: "cockroachdb" + tlsRejectUnauthorized: "1" + sshDomain: "" + registryAddr: "sealos.hub:5000" + monitorUrl: "http://launchpad-monitor.sealos.svc.cluster.local:8428" + accountUrl: "http://account-service.account-system.svc.cluster.local:2333" + rootRuntimeNamespace: "devbox-system" + currencySymbol: "usd" + gpuEnable: false + storageLimit: "10Gi" + runtimeClassName: "devbox-runtime" + retagSvcUrl: "http://devbox-service.devbox-system.svc.cluster.local:8092" + jwtSecret: "" + regionUid: "" + enableImportFeature: false + enableWebideFeature: false + enableAdvancedConfig: false + cpuSlideMarkList: "1,2,4,8,16" + memorySlideMarkList: "2,4,8,16,32" + devboxDomainChallengeSecret: "default-dev-secret-change-in-production" + nfsStorageClassName: "nfs-csi" + customScripts: "[]" + documentUrlZh: "https://sealos.run/docs/overview/intro" + documentUrlEn: "https://sealos.io/docs/overview/intro" + privacyUrlZh: "https://sealos.run/docs/msa/privacy-policy" + privacyUrlEn: "https://sealos.io/docs/msa/privacy-policy" diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/_helpers.tpl b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..f5ef9c687 --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "devbox-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "devbox-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "devbox-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "devbox-frontend.labels" -}} +helm.sh/chart: {{ include "devbox-frontend.chart" . }} +{{ include "devbox-frontend.selectorLabels" . }} +{{ include "devbox-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "devbox-frontend.selectorLabels" -}} +app: {{ include "devbox-frontend.fullname" . }} +{{- end }} + +{{- define "devbox-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "devbox-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "devbox-frontend.scheme" -}} +{{- if eq (toString .Values.devboxConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "devbox-frontend.port" -}} +{{- $scheme := include "devbox-frontend.scheme" . -}} +{{- $port := toString .Values.devboxConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.devboxConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "devbox-frontend.portSuffix" -}} +{{- $port := include "devbox-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "devbox-frontend.portEnv" -}} +{{- $port := include "devbox-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "devbox-frontend.cloudOrigin" -}} +{{- include "devbox-frontend.scheme" . -}}://{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} +{{- end }} + +{{- define "devbox-frontend.wildcardCloudOrigin" -}} +{{- include "devbox-frontend.scheme" . -}}://*.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} +{{- end }} + +{{- define "devbox-frontend.host" -}} +{{- default (printf "devbox.%s" .Values.devboxConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "devbox-frontend.appUrl" -}} +{{- include "devbox-frontend.scheme" . -}}://{{ include "devbox-frontend.host" . }}{{ include "devbox-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/devbox/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/app.yaml similarity index 55% rename from frontend/providers/devbox/deploy/manifests/appcr.yaml.tmpl rename to frontend/providers/devbox/deploy/charts/devbox-frontend/templates/app.yaml index dceb28fc6..c7cfa90a3 100644 --- a/frontend/providers/devbox/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/app.yaml @@ -6,13 +6,14 @@ metadata: spec: data: desc: DevBox - url: 'https://devbox.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}' - icon: 'https://devbox.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg' + url: {{ include "devbox-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "devbox-frontend.appUrl" .) | quote }} i18n: zh: name: DevBox zh-Hans: name: DevBox + menuData: name: DevBox type: iframe displayType: normal diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/challenge-ingress.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/challenge-ingress.yaml new file mode 100644 index 000000000..ef8433081 --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/challenge-ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + name: devbox-challenge + namespace: {{ .Release.Namespace }} +spec: + rules: + - host: "" + http: + paths: + - backend: + service: + name: {{ include "devbox-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + path: /api/.well-known/devbox-domain-challenge + pathType: Prefix diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/configmap.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/configmap.yaml new file mode 100644 index 000000000..39c92e73f --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "devbox-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "devbox-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + addr: :3000 diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/deployment.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/deployment.yaml new file mode 100644 index 000000000..39ce5fb11 --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/deployment.yaml @@ -0,0 +1,156 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "devbox-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "devbox-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "devbox-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + {{- include "devbox-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + - name: devbox-frontend-init + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + env: + - name: DATABASE_URL + value: {{ .Values.devboxConfig.databaseUrl | quote }} + - name: DATABASE_PROVIDER + value: {{ .Values.devboxConfig.databaseProvider | quote }} + command: + - sh + - -c + args: + - |- + cd /app/providers/devbox + DB_PROVIDER="${DATABASE_PROVIDER:-cockroachdb}" + SCHEMA_PATH="./prisma/cockroach/schema.prisma" + if [ "$DB_PROVIDER" = "postgresql" ] || [ "$DB_PROVIDER" = "postgres" ] || [ "$DB_PROVIDER" = "pg" ]; then + SCHEMA_PATH="./prisma/postgresql/schema.prisma" + HAS_UUID_FN=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM pg_proc WHERE proname='gen_random_uuid' LIMIT 1;") + if [ "$HAS_UUID_FN" != "1" ]; then + psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -c 'CREATE OR REPLACE FUNCTION public.gen_random_uuid() RETURNS uuid LANGUAGE SQL VOLATILE AS $func$ SELECT md5(random()::text || clock_timestamp()::text || txid_current()::text)::uuid; $func$;' + fi + MIGRATION="20260125094114_add_template_repository_kind" + HAS_MIGRATION=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM _prisma_migrations WHERE migration_name='${MIGRATION}' AND rolled_back_at IS NULL LIMIT 1;") + if [ "$HAS_MIGRATION" = "1" ]; then + prisma migrate deploy --schema "$SCHEMA_PATH" + exit 0 + fi + HAS_ENUM=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM pg_enum e JOIN pg_type t ON t.oid = e.enumtypid WHERE t.typname IN ('TemplateRepositoryKind','template_repository_kind') AND e.enumlabel = 'SERVICE' LIMIT 1;") + if [ "$HAS_ENUM" = "1" ]; then + prisma migrate resolve --schema "$SCHEMA_PATH" --applied "$MIGRATION" + fi + fi + prisma migrate deploy --schema "$SCHEMA_PATH" + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: NODE_TLS_REJECT_UNAUTHORIZED + value: {{ .Values.devboxConfig.tlsRejectUnauthorized | quote }} + - name: DOCUMENT_URL_ZH + value: {{ .Values.devboxConfig.documentUrlZh | quote }} + - name: DOCUMENT_URL_EN + value: {{ .Values.devboxConfig.documentUrlEn | quote }} + - name: PRIVACY_URL_ZH + value: {{ .Values.devboxConfig.privacyUrlZh | quote }} + - name: PRIVACY_URL_EN + value: {{ .Values.devboxConfig.privacyUrlEn | quote }} + - name: SEALOS_DOMAIN + value: {{ .Values.devboxConfig.cloudDomain | quote }} + - name: SSH_DOMAIN + value: {{ .Values.devboxConfig.sshDomain | quote }} + - name: INGRESS_SECRET + value: {{ .Values.devboxConfig.certSecretName | quote }} + - name: REGISTRY_ADDR + value: {{ .Values.devboxConfig.registryAddr | quote }} + - name: DEVBOX_AFFINITY_ENABLE + value: {{ "true" | quote }} + - name: MONITOR_URL + value: {{ .Values.devboxConfig.monitorUrl | quote }} + - name: ACCOUNT_URL + value: {{ .Values.devboxConfig.accountUrl | quote }} + - name: ROOT_RUNTIME_NAMESPACE + value: {{ .Values.devboxConfig.rootRuntimeNamespace | quote }} + - name: INGRESS_DOMAIN + value: {{ .Values.devboxConfig.cloudDomain | quote }} + - name: CURRENCY_SYMBOL + value: {{ .Values.devboxConfig.currencySymbol | quote }} + - name: GPU_ENABLE + value: {{ .Values.devboxConfig.gpuEnable | quote }} + - name: STORAGE_LIMIT + value: {{ .Values.devboxConfig.storageLimit | quote }} + - name: DEVBOX_RUNTIME_CLASS_NAME + value: {{ .Values.devboxConfig.runtimeClassName | quote }} + - name: RETAG_SVC_URL + value: {{ .Values.devboxConfig.retagSvcUrl | quote }} + - name: JWT_SECRET + value: {{ .Values.devboxConfig.jwtSecret | quote }} + - name: REGION_UID + value: {{ .Values.devboxConfig.regionUid | quote }} + - name: DATABASE_URL + value: {{ .Values.devboxConfig.databaseUrl | quote }} + - name: DATABASE_PROVIDER + value: {{ .Values.devboxConfig.databaseProvider | quote }} + - name: ENABLE_IMPORT_FEATURE + value: {{ .Values.devboxConfig.enableImportFeature | quote }} + - name: ENABLE_WEBIDE_FEATURE + value: {{ .Values.devboxConfig.enableWebideFeature | quote }} + - name: ENABLE_ADVANCED_CONFIG + value: {{ .Values.devboxConfig.enableAdvancedConfig | quote }} + - name: CPU_SLIDE_MARK_LIST + value: {{ .Values.devboxConfig.cpuSlideMarkList | quote }} + - name: MEMORY_SLIDE_MARK_LIST + value: {{ .Values.devboxConfig.memorySlideMarkList | quote }} + - name: DEVBOX_DOMAIN_CHALLENGE_SECRET + value: {{ .Values.devboxConfig.devboxDomainChallengeSecret | quote }} + - name: NFS_STORAGE_CLASS_NAME + value: {{ .Values.devboxConfig.nfsStorageClassName | quote }} + - name: CUSTOM_SCRIPTS + value: {{ .Values.devboxConfig.customScripts | quote }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + volumeMounts: + - name: devbox-frontend-volume + mountPath: /config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: devbox-frontend-volume + configMap: + name: {{ include "devbox-frontend.fullname" . }}-config diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/ingress.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/ingress.yaml new file mode 100644 index 000000000..269049fc2 --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "devbox-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "{{ include "devbox-frontend.cloudOrigin" . }}, {{ include "devbox-frontend.wildcardCloudOrigin" . }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "devbox-frontend.cloudOrigin" . }} {{ include "devbox-frontend.wildcardCloudOrigin" . }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} {{ .Values.devboxConfig.cloudDomain }}{{ include "devbox-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "devbox-frontend.cloudOrigin" . }} {{ include "devbox-frontend.wildcardCloudOrigin" . }}" + X-Xss-Protection "1; mode=block" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "devbox-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "devbox-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.devboxConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "devbox-frontend.host" . | quote }} + secretName: {{ .Values.devboxConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/service.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/service.yaml new file mode 100644 index 000000000..f9b4c4566 --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "devbox-frontend.labels" . | nindent 4 }} + name: {{ include "devbox-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "devbox-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/devbox/deploy/charts/devbox-frontend/values.yaml b/frontend/providers/devbox/deploy/charts/devbox-frontend/values.yaml new file mode 100644 index 000000000..d34feeed0 --- /dev/null +++ b/frontend/providers/devbox/deploy/charts/devbox-frontend/values.yaml @@ -0,0 +1,88 @@ +# Default values for devbox-frontend helm chart. + +image: ghcr.io/labring/sealos-devbox-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "devbox-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +devboxConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + databaseUrl: "" + databaseProvider: "cockroachdb" + tlsRejectUnauthorized: "1" + sshDomain: "" + registryAddr: "sealos.hub:5000" + monitorUrl: "http://launchpad-monitor.sealos.svc.cluster.local:8428" + accountUrl: "http://account-service.account-system.svc.cluster.local:2333" + rootRuntimeNamespace: "devbox-system" + currencySymbol: "usd" + gpuEnable: false + storageLimit: "10Gi" + runtimeClassName: "devbox-runtime" + retagSvcUrl: "http://devbox-service.devbox-system.svc.cluster.local:8092" + jwtSecret: "" + regionUid: "" + enableImportFeature: false + enableWebideFeature: false + enableAdvancedConfig: false + cpuSlideMarkList: "1,2,4,8,16" + memorySlideMarkList: "2,4,8,16,32" + devboxDomainChallengeSecret: "default-dev-secret-change-in-production" + nfsStorageClassName: "nfs-csi" + customScripts: "[]" + documentUrlZh: "https://sealos.run/docs/overview/intro" + documentUrlEn: "https://sealos.io/docs/overview/intro" + privacyUrlZh: "https://sealos.run/docs/msa/privacy-policy" + privacyUrlEn: "https://sealos.io/docs/msa/privacy-policy" + +resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 100m + memory: 128Mi + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - devbox-frontend + topologyKey: "kubernetes.io/hostname" diff --git a/frontend/providers/devbox/deploy/devbox-frontend-entrypoint.sh b/frontend/providers/devbox/deploy/devbox-frontend-entrypoint.sh new file mode 100755 index 000000000..3d7a8c420 --- /dev/null +++ b/frontend/providers/devbox/deploy/devbox-frontend-entrypoint.sh @@ -0,0 +1,117 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"devbox-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"devbox-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/devbox-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string devboxConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string devboxConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string devboxConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string devboxConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string devboxConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" +SEALOS_DATABASE_GLOBAL_COCKROACHDB_URI=${SEALOS_DATABASE_GLOBAL_COCKROACHDB_URI:-"${databaseGlobalCockroachdbURI:-$(get_cm_value sealos-system sealos-config databaseGlobalCockroachdbURI)}"} +add_set_string devboxConfig.databaseUrl "${SEALOS_DATABASE_GLOBAL_COCKROACHDB_URI}" +SEALOS_JWT_INTERNAL=${SEALOS_JWT_INTERNAL:-"${jwtInternal:-$(get_cm_value sealos-system sealos-config jwtInternal)}"} +add_set_string devboxConfig.jwtSecret "${SEALOS_JWT_INTERNAL}" +SEALOS_REGION_UID=${SEALOS_REGION_UID:-"${regionUid:-${regionUID:-$(get_cm_value sealos-system sealos-config regionUID)}}"} +add_set_string devboxConfig.regionUid "${SEALOS_REGION_UID}" +add_set_string devboxConfig.databaseProvider "${databaseProvider:-}" +add_set_string devboxConfig.tlsRejectUnauthorized "${tlsRejectUnauthorized:-}" +add_set_string devboxConfig.sshDomain "${sshDomain:-}" +add_set_string devboxConfig.registryAddr "${registryAddr:-}" +add_set_string devboxConfig.monitorUrl "${monitorUrl:-}" +add_set_string devboxConfig.accountUrl "${accountUrl:-}" +add_set_string devboxConfig.rootRuntimeNamespace "${rootRuntimeNamespace:-}" +add_set_string devboxConfig.currencySymbol "${currencySymbol:-}" +add_set_string devboxConfig.gpuEnable "${gpuEnable:-}" +add_set_string devboxConfig.storageLimit "${storageLimit:-}" +add_set_string devboxConfig.runtimeClassName "${runtimeClassName:-}" +add_set_string devboxConfig.retagSvcUrl "${retagSvcUrl:-}" +add_set_string devboxConfig.enableImportFeature "${enableImportFeature:-}" +add_set_string devboxConfig.enableWebideFeature "${enableWebideFeature:-}" +add_set_string devboxConfig.enableAdvancedConfig "${enableAdvancedConfig:-}" +add_set_string devboxConfig.cpuSlideMarkList "${cpuSlideMarkList:-}" +add_set_string devboxConfig.memorySlideMarkList "${memorySlideMarkList:-}" +add_set_string devboxConfig.devboxDomainChallengeSecret "${devboxDomainChallengeSecret:-}" +add_set_string devboxConfig.nfsStorageClassName "${nfsStorageClassName:-}" +add_set_string devboxConfig.customScripts "${customScripts:-}" +add_set_string devboxConfig.documentUrlZh "${documentUrlZh:-}" +add_set_string devboxConfig.documentUrlEn "${documentUrlEn:-}" +add_set_string devboxConfig.privacyUrlZh "${privacyUrlZh:-}" +add_set_string devboxConfig.privacyUrlEn "${privacyUrlEn:-}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap devbox-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service devbox-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment devbox-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress devbox-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress devbox-challenge +fi + +adopt_namespaced_resource app-system apps.app.sealos.io devbox + + +SERVICE_NAME="devbox-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/devbox-frontend/devbox-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/devbox-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/devbox/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/devbox/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index 1035208e7..000000000 --- a/frontend/providers/devbox/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,189 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: devbox-frontend - name: devbox-frontend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: devbox-frontend-config - namespace: devbox-frontend -data: - config.yaml: |- - addr: :3000 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: devbox-frontend - namespace: devbox-frontend -spec: - replicas: 1 - selector: - matchLabels: - app: devbox-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 25% - maxSurge: 25% - template: - metadata: - labels: - app: devbox-frontend - spec: - initContainers: - - name: devbox-frontend-init - image: ghcr.io/labring/sealos-devbox-frontend:latest - imagePullPolicy: Always - env: - - name: DATABASE_URL - value: {{ .databaseUrl }} # -nsealos cm desktop-frontend-config ->database->global + devbox - - name: DATABASE_PROVIDER - value: "{{ default "cockroachdb" .databaseProvider }}" - command: - - sh - - -c - args: - - |- - cd /app/providers/devbox - DB_PROVIDER="${DATABASE_PROVIDER:-cockroachdb}" - SCHEMA_PATH="./prisma/cockroach/schema.prisma" - if [ "$DB_PROVIDER" = "postgresql" ] || [ "$DB_PROVIDER" = "postgres" ] || [ "$DB_PROVIDER" = "pg" ]; then - SCHEMA_PATH="./prisma/postgresql/schema.prisma" - HAS_UUID_FN=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM pg_proc WHERE proname='gen_random_uuid' LIMIT 1;") - if [ "$HAS_UUID_FN" != "1" ]; then - psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -c 'CREATE OR REPLACE FUNCTION public.gen_random_uuid() RETURNS uuid LANGUAGE SQL VOLATILE AS $func$ SELECT md5(random()::text || clock_timestamp()::text || txid_current()::text)::uuid; $func$;' - fi - MIGRATION="20260125094114_add_template_repository_kind" - HAS_MIGRATION=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM _prisma_migrations WHERE migration_name='${MIGRATION}' AND rolled_back_at IS NULL LIMIT 1;") - if [ "$HAS_MIGRATION" = "1" ]; then - prisma migrate deploy --schema "$SCHEMA_PATH" - exit 0 - fi - HAS_ENUM=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM pg_enum e JOIN pg_type t ON t.oid = e.enumtypid WHERE t.typname IN ('TemplateRepositoryKind','template_repository_kind') AND e.enumlabel = 'SERVICE' LIMIT 1;") - if [ "$HAS_ENUM" = "1" ]; then - prisma migrate resolve --schema "$SCHEMA_PATH" --applied "$MIGRATION" - fi - fi - prisma migrate deploy --schema "$SCHEMA_PATH" - containers: - - name: devbox-frontend - env: - - name: NODE_TLS_REJECT_UNAUTHORIZED - value: "{{ default "1" .tlsRejectUnauthorized }}" - - name: DOCUMENT_URL_ZH - value: https://sealos.run/docs/overview/intro - - name: DOCUMENT_URL_EN - value: https://sealos.io/docs/overview/intro - - name: PRIVACY_URL_ZH - value: https://sealos.run/docs/msa/privacy-policy - - name: PRIVACY_URL_EN - value: https://sealos.io/docs/msa/privacy-policy - - name: SEALOS_DOMAIN - value: {{ .cloudDomain }} - - name: SSH_DOMAIN - value: {{ default "" .sshDomain }} - - name: INGRESS_SECRET - value: wildcard-cert - - name: REGISTRY_ADDR - value: {{ .registryAddr }} - - name: DEVBOX_AFFINITY_ENABLE - value: 'true' - - name: MONITOR_URL - value: http://launchpad-monitor.sealos.svc.cluster.local:8428 - - name: ACCOUNT_URL - value: http://account-service.account-system.svc.cluster.local:2333 - - name: ROOT_RUNTIME_NAMESPACE - value: devbox-system - - name: INGRESS_DOMAIN - value: {{ .cloudDomain }} - - name: CURRENCY_SYMBOL - value: usd # 'shellCoin' | 'cny' | 'usd' - - name: GPU_ENABLE - value: 'false' - - name: STORAGE_LIMIT - value: 10Gi # default is 10Gi - - name: DEVBOX_RUNTIME_CLASS_NAME - value: devbox-runtime - - name: RETAG_SVC_URL - value: http://devbox-service.devbox-system.svc.cluster.local:8092 - - name: JWT_SECRET - value: {{ .jwtSecret }} # -nsealos cm desktop-frontend-config ->jwt->Internal - - name: REGION_UID - value: {{ .regionUid }} # -nsealos cm desktop-frontend-config ->regionUid - - name: DATABASE_URL - value: {{ .databaseUrl }} # -nsealos cm desktop-frontend-config ->database->global + devbox - - name: DATABASE_PROVIDER - value: "{{ default "cockroachdb" .databaseProvider }}" - - name: ENABLE_IMPORT_FEATURE - value: 'false' - - name: ENABLE_WEBIDE_FEATURE - value: 'false' - - name: ENABLE_ADVANCED_CONFIG - value: 'false' - - name: CPU_SLIDE_MARK_LIST - value: '1,2,4,8,16' - - name: MEMORY_SLIDE_MARK_LIST - value: '2,4,8,16,32' - - name: DEVBOX_DOMAIN_CHALLENGE_SECRET - value: {{ .devboxDomainChallengeSecret }} - - name: NFS_STORAGE_CLASS_NAME - value: {{ default "nfs-csi" .nfsStorageClassName }} - - name: CUSTOM_SCRIPTS - value: '{{ default "[]" .customScripts }}' - securityContext: - runAsNonRoot: true - runAsUser: 1001 - allowPrivilegeEscalation: false - capabilities: - drop: - - 'ALL' - resources: - limits: - cpu: 2000m - memory: 2048Mi - requests: - cpu: 100m - memory: 128Mi - # do not modify this image, it is used for CI/CD - image: ghcr.io/labring/sealos-devbox-frontend:latest - imagePullPolicy: Always - volumeMounts: - - name: devbox-frontend-volume - mountPath: /config.yaml - subPath: config.yaml - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: 'app' - operator: In - values: - - devbox-frontend - topologyKey: 'kubernetes.io/hostname' - volumes: - - name: devbox-frontend-volume - configMap: - name: devbox-frontend-config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: devbox-frontend - name: devbox-frontend - namespace: devbox-frontend -spec: - ports: - - name: http - port: 3000 - protocol: TCP - targetPort: 3000 - selector: - app: devbox-frontend diff --git a/frontend/providers/devbox/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/devbox/deploy/manifests/ingress.yaml.tmpl deleted file mode 100644 index b05a597fe..000000000 --- a/frontend/providers/devbox/deploy/manifests/ingress.yaml.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/configuration-snippet: | - more_clear_headers "X-Frame-Options:"; - more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com; frame-src 'self' {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; - more_set_headers "X-Xss-Protection: 1; mode=block"; - higress.io/response-header-control-remove: X-Frame-Options - higress.io/response-header-control-update: | - Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com; frame-src 'self' {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - X-Xss-Protection "1; mode=block" - name: devbox-frontend - namespace: devbox-frontend -spec: - rules: - - host: devbox.{{ .cloudDomain }} - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: devbox-frontend - port: - number: 3000 - tls: - - hosts: - - devbox.{{ .cloudDomain }} - secretName: {{ .certSecretName }} # wildcard-cert ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - name: devbox-challenge - namespace: devbox-frontend -spec: - rules: - - host: "" - http: - paths: - - backend: - service: - name: devbox-frontend - port: - number: 3000 - path: /api/.well-known/devbox-domain-challenge - pathType: Prefix diff --git a/frontend/providers/invite/deploy/Kubefile b/frontend/providers/invite/deploy/Kubefile index ec1504e7d..54276a98f 100644 --- a/frontend/providers/invite/deploy/Kubefile +++ b/frontend/providers/invite/deploy/Kubefile @@ -3,11 +3,7 @@ FROM scratch USER 65532:65532 COPY registry registry -COPY manifests manifests +COPY charts charts +COPY invite-frontend-entrypoint.sh invite-frontend-entrypoint.sh -ENV cloudDomain="127.0.0.1.nip.io" -ENV cloudPort="" -ENV certSecretName="wildcard-cert" -ENV lafBaseUrl="" - -CMD ["kubectl apply -f manifests"] +CMD ["bash invite-frontend-entrypoint.sh"] diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/Chart.yaml b/frontend/providers/invite/deploy/charts/invite-frontend/Chart.yaml new file mode 100644 index 000000000..1b5882777 --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: invite-frontend +description: Sealos invite Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/invite-frontend-values.yaml b/frontend/providers/invite/deploy/charts/invite-frontend/invite-frontend-values.yaml new file mode 100644 index 000000000..13e8de202 --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/invite-frontend-values.yaml @@ -0,0 +1,16 @@ +# Custom values for invite-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +inviteConfig: + lafBaseUrl: "" + giftRatio: "0.1" diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/templates/_helpers.tpl b/frontend/providers/invite/deploy/charts/invite-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..77ac92512 --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "invite-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "invite-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "invite-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "invite-frontend.labels" -}} +helm.sh/chart: {{ include "invite-frontend.chart" . }} +{{ include "invite-frontend.selectorLabels" . }} +{{ include "invite-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "invite-frontend.selectorLabels" -}} +app: {{ include "invite-frontend.fullname" . }} +{{- end }} + +{{- define "invite-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "invite-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "invite-frontend.scheme" -}} +{{- if eq (toString .Values.inviteConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "invite-frontend.port" -}} +{{- $scheme := include "invite-frontend.scheme" . -}} +{{- $port := toString .Values.inviteConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.inviteConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "invite-frontend.portSuffix" -}} +{{- $port := include "invite-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "invite-frontend.portEnv" -}} +{{- $port := include "invite-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "invite-frontend.cloudOrigin" -}} +{{- include "invite-frontend.scheme" . -}}://{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} +{{- end }} + +{{- define "invite-frontend.wildcardCloudOrigin" -}} +{{- include "invite-frontend.scheme" . -}}://*.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} +{{- end }} + +{{- define "invite-frontend.host" -}} +{{- default (printf "invite.%s" .Values.inviteConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "invite-frontend.appUrl" -}} +{{- include "invite-frontend.scheme" . -}}://{{ include "invite-frontend.host" . }}{{ include "invite-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/invite/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/invite/deploy/charts/invite-frontend/templates/app.yaml similarity index 54% rename from frontend/providers/invite/deploy/manifests/appcr.yaml.tmpl rename to frontend/providers/invite/deploy/charts/invite-frontend/templates/app.yaml index 0c933c433..c9ed09821 100644 --- a/frontend/providers/invite/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/invite/deploy/charts/invite-frontend/templates/app.yaml @@ -6,12 +6,14 @@ metadata: spec: data: desc: invite - url: "https://invite.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - icon: "https://invite.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg" + url: {{ include "invite-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "invite-frontend.appUrl" .) | quote }} + i18n: + zh: + name: 邀请注册 + zh-Hans: + name: 邀请注册 menuData: name: invite type: iframe displayType: normal - i18n: - zh: - name: 邀请注册 diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/templates/configmap.yaml b/frontend/providers/invite/deploy/charts/invite-frontend/templates/configmap.yaml new file mode 100644 index 000000000..52a7751be --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/templates/configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "invite-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "invite-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + addr: :3000 diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/templates/deployment.yaml b/frontend/providers/invite/deploy/charts/invite-frontend/templates/deployment.yaml new file mode 100644 index 000000000..b1c4c1e81 --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/templates/deployment.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "invite-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "invite-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "invite-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + {{- include "invite-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: SEALOS_DOMAIN + value: {{ .Values.inviteConfig.cloudDomain | quote }} + - name: LAF_BASE_URL + value: {{ .Values.inviteConfig.lafBaseUrl | quote }} + - name: GIFT_RATIO + value: {{ .Values.inviteConfig.giftRatio | quote }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + volumeMounts: + - name: invite-frontend-volume + mountPath: /config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: invite-frontend-volume + configMap: + name: {{ include "invite-frontend.fullname" . }}-config diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/templates/ingress.yaml b/frontend/providers/invite/deploy/charts/invite-frontend/templates/ingress.yaml new file mode 100644 index 000000000..d007c5ebd --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "invite-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "{{ include "invite-frontend.cloudOrigin" . }}, {{ include "invite-frontend.wildcardCloudOrigin" . }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "invite-frontend.cloudOrigin" . }} {{ include "invite-frontend.wildcardCloudOrigin" . }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} {{ .Values.inviteConfig.cloudDomain }}{{ include "invite-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "invite-frontend.cloudOrigin" . }} {{ include "invite-frontend.wildcardCloudOrigin" . }}" + X-Xss-Protection "1; mode=block" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "invite-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "invite-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.inviteConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "invite-frontend.host" . | quote }} + secretName: {{ .Values.inviteConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/templates/service.yaml b/frontend/providers/invite/deploy/charts/invite-frontend/templates/service.yaml new file mode 100644 index 000000000..52c662713 --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "invite-frontend.labels" . | nindent 4 }} + name: {{ include "invite-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "invite-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/invite/deploy/charts/invite-frontend/values.yaml b/frontend/providers/invite/deploy/charts/invite-frontend/values.yaml new file mode 100644 index 000000000..3e5ed0db3 --- /dev/null +++ b/frontend/providers/invite/deploy/charts/invite-frontend/values.yaml @@ -0,0 +1,52 @@ +# Default values for invite-frontend helm chart. + +image: ghcr.io/labring/sealos-invite-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "invite-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +inviteConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + lafBaseUrl: "" + giftRatio: "0.1" + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +affinity: {} diff --git a/frontend/providers/invite/deploy/invite-frontend-entrypoint.sh b/frontend/providers/invite/deploy/invite-frontend-entrypoint.sh new file mode 100755 index 000000000..d783b1583 --- /dev/null +++ b/frontend/providers/invite/deploy/invite-frontend-entrypoint.sh @@ -0,0 +1,88 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"invite-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"invite-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/invite-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string inviteConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string inviteConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string inviteConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string inviteConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string inviteConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" +add_set_string inviteConfig.lafBaseUrl "${lafBaseUrl:-}" +add_set_string inviteConfig.giftRatio "${giftRatio:-}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap invite-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service invite-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment invite-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress invite-frontend +fi + +adopt_namespaced_resource app-system apps.app.sealos.io invite + + +SERVICE_NAME="invite-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/invite-frontend/invite-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/invite-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/invite/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/invite/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index 87565615a..000000000 --- a/frontend/providers/invite/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,86 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: invite-frontend - name: invite-frontend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: invite-frontend-config - namespace: invite-frontend -data: - config.yaml: |- - addr: :3000 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: invite-frontend - namespace: invite-frontend -spec: - replicas: 1 - selector: - matchLabels: - app: invite-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 25% - maxSurge: 25% - template: - metadata: - labels: - app: invite-frontend - spec: - containers: - - name: invite-frontend - env: - - name: SEALOS_DOMAIN - value: {{ .cloudDomain }} - - name: LAF_BASE_URL - value: {{ .lafBaseUrl }} - - name: GIFT_RATIO - value: "0.1" - securityContext: - runAsNonRoot: true - runAsUser: 1001 - allowPrivilegeEscalation: false - capabilities: - drop: - - 'ALL' - resources: - limits: - cpu: 1000m - memory: 1024Mi - requests: - cpu: 10m - memory: 128Mi - # do not modify this image, it is used for CI/CD - image: ghcr.io/labring/sealos-invite-frontend:latest - imagePullPolicy: Always - volumeMounts: - - name: invite-frontend-volume - mountPath: /config.yaml - subPath: config.yaml - volumes: - - name: invite-frontend-volume - configMap: - name: invite-frontend-config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: invite-frontend - name: invite-frontend - namespace: invite-frontend -spec: - ports: - - name: http - port: 3000 - protocol: TCP - targetPort: 3000 - selector: - app: invite-frontend diff --git a/frontend/providers/invite/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/invite/deploy/manifests/ingress.yaml.tmpl deleted file mode 100644 index 76f93d40e..000000000 --- a/frontend/providers/invite/deploy/manifests/ingress.yaml.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright © 2023 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/enable-cors: "true" - nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" - nginx.ingress.kubernetes.io/cors-allow-origin: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}, https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - nginx.ingress.kubernetes.io/cors-allow-credentials: "true" - nginx.ingress.kubernetes.io/cors-max-age: "600" - nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - nginx.ingress.kubernetes.io/configuration-snippet: | - more_clear_headers "X-Frame-Options:"; - more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; - more_set_headers "X-Xss-Protection: 1; mode=block"; - higress.io/response-header-control-remove: X-Frame-Options - higress.io/response-header-control-update: | - Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - X-Xss-Protection "1; mode=block" - name: invite-frontend - namespace: invite-frontend -spec: - rules: - - host: invite.{{ .cloudDomain }} - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: invite-frontend - port: - number: 3000 - tls: - - hosts: - - invite.{{ .cloudDomain }} - secretName: {{ .certSecretName }} diff --git a/frontend/providers/kubepanel/deploy/Kubefile b/frontend/providers/kubepanel/deploy/Kubefile index 2b57e8fd7..1df32281e 100644 --- a/frontend/providers/kubepanel/deploy/Kubefile +++ b/frontend/providers/kubepanel/deploy/Kubefile @@ -2,11 +2,8 @@ FROM scratch USER 65532:65532 -COPY manifests manifests COPY registry registry +COPY charts charts +COPY kubepanel-frontend-entrypoint.sh kubepanel-frontend-entrypoint.sh -ENV cloudDomain="127.0.0.1.nip.io" -ENV cloudPort="" -ENV certSecretName="wildcard-cert" - -CMD ["kubectl apply -f manifests"] \ No newline at end of file +CMD ["bash kubepanel-frontend-entrypoint.sh"] diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/Chart.yaml b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/Chart.yaml new file mode 100644 index 000000000..8cd88d22b --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: kubepanel-frontend +description: Sealos Kube Panel Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/kubepanel-frontend-values.yaml b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/kubepanel-frontend-values.yaml new file mode 100644 index 000000000..ebbfab230 --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/kubepanel-frontend-values.yaml @@ -0,0 +1,14 @@ +# Custom values for kubepanel-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +kubepanelConfig: diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/_helpers.tpl b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..7bbd858d3 --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "kubepanel-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "kubepanel-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "kubepanel-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "kubepanel-frontend.labels" -}} +helm.sh/chart: {{ include "kubepanel-frontend.chart" . }} +{{ include "kubepanel-frontend.selectorLabels" . }} +{{ include "kubepanel-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "kubepanel-frontend.selectorLabels" -}} +app: {{ include "kubepanel-frontend.fullname" . }} +{{- end }} + +{{- define "kubepanel-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "kubepanel-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "kubepanel-frontend.scheme" -}} +{{- if eq (toString .Values.kubepanelConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "kubepanel-frontend.port" -}} +{{- $scheme := include "kubepanel-frontend.scheme" . -}} +{{- $port := toString .Values.kubepanelConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.kubepanelConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "kubepanel-frontend.portSuffix" -}} +{{- $port := include "kubepanel-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "kubepanel-frontend.portEnv" -}} +{{- $port := include "kubepanel-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "kubepanel-frontend.cloudOrigin" -}} +{{- include "kubepanel-frontend.scheme" . -}}://{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} +{{- end }} + +{{- define "kubepanel-frontend.wildcardCloudOrigin" -}} +{{- include "kubepanel-frontend.scheme" . -}}://*.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} +{{- end }} + +{{- define "kubepanel-frontend.host" -}} +{{- default (printf "kubepanel.%s" .Values.kubepanelConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "kubepanel-frontend.appUrl" -}} +{{- include "kubepanel-frontend.scheme" . -}}://{{ include "kubepanel-frontend.host" . }}{{ include "kubepanel-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/kubepanel/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/app.yaml similarity index 58% rename from frontend/providers/kubepanel/deploy/manifests/appcr.yaml.tmpl rename to frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/app.yaml index 10b2cb3b0..615b82161 100644 --- a/frontend/providers/kubepanel/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/app.yaml @@ -6,8 +6,8 @@ metadata: spec: data: desc: Kube Panel - url: "https://kubepanel.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - icon: "https://kubepanel.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg" + url: {{ include "kubepanel-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "kubepanel-frontend.appUrl" .) | quote }} i18n: zh: name: 资源监控 diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/configmap.yaml b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/configmap.yaml new file mode 100644 index 000000000..7bd074f04 --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "kubepanel-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "kubepanel-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + addr: :3000 diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/deployment.yaml b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/deployment.yaml new file mode 100644 index 000000000..76601c3b3 --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/deployment.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "kubepanel-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "kubepanel-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "kubepanel-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + {{- include "kubepanel-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: SEALOS_DOMAIN + value: {{ .Values.kubepanelConfig.cloudDomain | quote }} + - name: SEALOS_PORT + value: {{ include "kubepanel-frontend.portEnv" . | quote }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + volumeMounts: + - name: kubepanel-frontend-volume + mountPath: /config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: kubepanel-frontend-volume + configMap: + name: {{ include "kubepanel-frontend.fullname" . }}-config diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/ingress.yaml b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/ingress.yaml new file mode 100644 index 000000000..5724f2d4d --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "kubepanel-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "{{ include "kubepanel-frontend.cloudOrigin" . }}, {{ include "kubepanel-frontend.wildcardCloudOrigin" . }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "kubepanel-frontend.cloudOrigin" . }} {{ include "kubepanel-frontend.wildcardCloudOrigin" . }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} {{ .Values.kubepanelConfig.cloudDomain }}{{ include "kubepanel-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "kubepanel-frontend.cloudOrigin" . }} {{ include "kubepanel-frontend.wildcardCloudOrigin" . }}" + X-Xss-Protection "1; mode=block" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "kubepanel-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "kubepanel-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.kubepanelConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "kubepanel-frontend.host" . | quote }} + secretName: {{ .Values.kubepanelConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/service.yaml b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/service.yaml new file mode 100644 index 000000000..2fdcc35cb --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "kubepanel-frontend.labels" . | nindent 4 }} + name: {{ include "kubepanel-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "kubepanel-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/values.yaml b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/values.yaml new file mode 100644 index 000000000..e2d893f5d --- /dev/null +++ b/frontend/providers/kubepanel/deploy/charts/kubepanel-frontend/values.yaml @@ -0,0 +1,61 @@ +# Default values for kubepanel-frontend helm chart. + +image: ghcr.io/labring/sealos-kubepanel-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "kubepanel-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +kubepanelConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + +resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - kubepanel-frontend + topologyKey: "kubernetes.io/hostname" diff --git a/frontend/providers/kubepanel/deploy/kubepanel-frontend-entrypoint.sh b/frontend/providers/kubepanel/deploy/kubepanel-frontend-entrypoint.sh new file mode 100755 index 000000000..a28975170 --- /dev/null +++ b/frontend/providers/kubepanel/deploy/kubepanel-frontend-entrypoint.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"kubepanel-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"kubepanel-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/kubepanel-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string kubepanelConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string kubepanelConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string kubepanelConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string kubepanelConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string kubepanelConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" + + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap kubepanel-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service kubepanel-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment kubepanel-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress kubepanel-frontend +fi + +adopt_namespaced_resource app-system apps.app.sealos.io kubepanel + + +SERVICE_NAME="kubepanel-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/kubepanel-frontend/kubepanel-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/kubepanel-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/kubepanel/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/kubepanel/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index d7b5df3b0..000000000 --- a/frontend/providers/kubepanel/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,96 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: kubepanel-frontend - name: kubepanel-frontend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: kubepanel-frontend-config - namespace: kubepanel-frontend -data: - config.yaml: |- - addr: :3000 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: kubepanel-frontend - namespace: kubepanel-frontend -spec: - replicas: 1 - selector: - matchLabels: - app: kubepanel-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 25% - maxSurge: 25% - template: - metadata: - labels: - app: kubepanel-frontend - spec: - containers: - - name: kubepanel-frontend - env: - - name: SEALOS_DOMAIN - value: {{ .cloudDomain }} - - name: SEALOS_PORT - value: "{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - securityContext: - runAsNonRoot: true - runAsUser: 1001 - allowPrivilegeEscalation: false - capabilities: - drop: - - 'ALL' - resources: - limits: - cpu: 1000m - memory: 1024Mi - requests: - cpu: 10m - memory: 128Mi - # do not modify this image, it is used for CI/CD - image: ghcr.io/labring/sealos-kubepanel-frontend:latest - imagePullPolicy: Always - volumeMounts: - - name: kubepanel-frontend-volume - mountPath: /config.yaml - subPath: config.yaml - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "app" - operator: In - values: - - kubepanel-frontend - topologyKey: "kubernetes.io/hostname" - volumes: - - name: kubepanel-frontend-volume - configMap: - name: kubepanel-frontend-config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: kubepanel-frontend - name: kubepanel-frontend - namespace: kubepanel-frontend -spec: - ports: - - name: http - port: 3000 - protocol: TCP - targetPort: 3000 - selector: - app: kubepanel-frontend diff --git a/frontend/providers/kubepanel/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/kubepanel/deploy/manifests/ingress.yaml.tmpl deleted file mode 100644 index 00390b799..000000000 --- a/frontend/providers/kubepanel/deploy/manifests/ingress.yaml.tmpl +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright © 2022 sealos. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/configuration-snippet: | - more_clear_headers "X-Frame-Options:"; - more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; - more_set_headers "X-Xss-Protection: 1; mode=block"; - higress.io/response-header-control-remove: X-Frame-Options - higress.io/response-header-control-update: | - Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - X-Xss-Protection "1; mode=block" - name: kubepanel-frontend - namespace: kubepanel-frontend -spec: - rules: - - host: kubepanel.{{ .cloudDomain }} - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: kubepanel-frontend - port: - number: 3000 - tls: - - hosts: - - kubepanel.{{ .cloudDomain }} - secretName: {{ .certSecretName }} diff --git a/frontend/providers/objectstorage/deploy/Kubefile b/frontend/providers/objectstorage/deploy/Kubefile index 464a59128..5f2459b20 100644 --- a/frontend/providers/objectstorage/deploy/Kubefile +++ b/frontend/providers/objectstorage/deploy/Kubefile @@ -3,9 +3,7 @@ FROM scratch USER 65532:65532 COPY registry registry -COPY manifests manifests +COPY charts charts +COPY object-storage-frontend-entrypoint.sh object-storage-frontend-entrypoint.sh -ENV cloudDomain=${cloudDomain:-"127.0.0.1.nip.io"} -ENV cloudPort=${cloudPort:-"443"} - -CMD ["kubectl apply -f manifests/deploy.yaml"] +CMD ["bash object-storage-frontend-entrypoint.sh"] diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/Chart.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/Chart.yaml new file mode 100644 index 000000000..d0111d213 --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: objectstorage-frontend +description: Sealos Object Storage Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/object-storage-frontend-values.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/object-storage-frontend-values.yaml new file mode 100644 index 000000000..cfc120b8d --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/object-storage-frontend-values.yaml @@ -0,0 +1,24 @@ +# Custom values for objectstorage-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 10m + memory: 128Mi + +objectstorageConfig: + monitorUrl: "http://object-storage-monitor.objectstorage-system.svc.cluster.local:9090/q" + billingUrl: "" + billingSecret: "" + appLaunchpadUrl: "http://applaunchpad-frontend.applaunchpad-frontend.svc.cluster.local:3000/api/v1alpha" + appTokenJwtKey: "" + hostingPodCpuMilliCores: 200 + hostingPodMemoryMiB: 128 + hostingAppNamePrefix: "static-host" + hostingNetworkProtocol: "HTTP" + hostingNetworkPort: 80 diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/_helpers.tpl b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..e73fccc3a --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "objectstorage-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "objectstorage-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "objectstorage-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "objectstorage-frontend.labels" -}} +helm.sh/chart: {{ include "objectstorage-frontend.chart" . }} +{{ include "objectstorage-frontend.selectorLabels" . }} +{{ include "objectstorage-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "objectstorage-frontend.selectorLabels" -}} +app: {{ include "objectstorage-frontend.fullname" . }} +{{- end }} + +{{- define "objectstorage-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "objectstorage-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "objectstorage-frontend.scheme" -}} +{{- if eq (toString .Values.objectstorageConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "objectstorage-frontend.port" -}} +{{- $scheme := include "objectstorage-frontend.scheme" . -}} +{{- $port := toString .Values.objectstorageConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.objectstorageConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "objectstorage-frontend.portSuffix" -}} +{{- $port := include "objectstorage-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "objectstorage-frontend.portEnv" -}} +{{- $port := include "objectstorage-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "objectstorage-frontend.cloudOrigin" -}} +{{- include "objectstorage-frontend.scheme" . -}}://{{ .Values.objectstorageConfig.cloudDomain }}{{ include "objectstorage-frontend.portSuffix" . }} +{{- end }} + +{{- define "objectstorage-frontend.wildcardCloudOrigin" -}} +{{- include "objectstorage-frontend.scheme" . -}}://*.{{ .Values.objectstorageConfig.cloudDomain }}{{ include "objectstorage-frontend.portSuffix" . }} +{{- end }} + +{{- define "objectstorage-frontend.host" -}} +{{- default (printf "objectstorage.%s" .Values.objectstorageConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "objectstorage-frontend.appUrl" -}} +{{- include "objectstorage-frontend.scheme" . -}}://{{ include "objectstorage-frontend.host" . }}{{ include "objectstorage-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/app.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/app.yaml new file mode 100644 index 000000000..0555816d5 --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/app.yaml @@ -0,0 +1,19 @@ +apiVersion: app.sealos.io/v1 +kind: App +metadata: + name: objectstorage + namespace: app-system +spec: + data: + desc: object storage + url: {{ include "objectstorage-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "objectstorage-frontend.appUrl" .) | quote }} + i18n: + zh: + name: 对象存储 + zh-Hans: + name: 对象存储 + menuData: + name: Object Storage + type: iframe + displayType: normal diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/configmap.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/configmap.yaml new file mode 100644 index 000000000..25d088fa2 --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/configmap.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "objectstorage-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "objectstorage-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + cloud: + domain: {{ .Values.objectstorageConfig.cloudDomain | quote }} + port: {{ default 443 .Values.objectstorageConfig.cloudPort }} + objectStorage: + resources: + hostingPodCpuMilliCores: {{ .Values.objectstorageConfig.hostingPodCpuMilliCores }} + hostingPodMemoryMiB: {{ .Values.objectstorageConfig.hostingPodMemoryMiB }} + components: + monitoring: + url: {{ .Values.objectstorageConfig.monitorUrl | quote }} + billing: + url: {{ .Values.objectstorageConfig.billingUrl | quote }} + secret: {{ .Values.objectstorageConfig.billingSecret | quote }} + appLaunchpad: + url: {{ .Values.objectstorageConfig.appLaunchpadUrl | quote }} + realName: + appTokenJwtKey: {{ .Values.objectstorageConfig.appTokenJwtKey | quote }} + hosting: + appNamePrefix: {{ .Values.objectstorageConfig.hostingAppNamePrefix | quote }} + networkProtocol: {{ .Values.objectstorageConfig.hostingNetworkProtocol | quote }} + networkPort: {{ .Values.objectstorageConfig.hostingNetworkPort }} diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/deployment.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/deployment.yaml new file mode 100644 index 000000000..178a285e7 --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/deployment.yaml @@ -0,0 +1,64 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "objectstorage-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "objectstorage-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + revisionHistoryLimit: 1 + selector: + matchLabels: + {{- include "objectstorage-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 0 + template: + metadata: + labels: + {{- include "objectstorage-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: MONITOR_URL + value: {{ .Values.objectstorageConfig.monitorUrl | quote }} + - name: APP_LAUNCHPAD_URL + value: {{ .Values.objectstorageConfig.appLaunchpadUrl | quote }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + volumeMounts: + - name: object-storage-frontend-volume + mountPath: /app/data/config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: object-storage-frontend-volume + configMap: + name: {{ include "objectstorage-frontend.fullname" . }}-config diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/ingress.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/ingress.yaml new file mode 100644 index 000000000..3c149bc59 --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/ingress.yaml @@ -0,0 +1,37 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "objectstorage-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/configuration-snippet: | + if ($request_uri ~* \.(js|css|gif|jpe?g|png)) { + expires 30d; + add_header Cache-Control "public"; + } + nginx.ingress.kubernetes.io/proxy-body-size: 3g + nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "180" + nginx.ingress.kubernetes.io/proxy-send-timeout: "180" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "objectstorage-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "objectstorage-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.objectstorageConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "objectstorage-frontend.host" . | quote }} + secretName: {{ .Values.objectstorageConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/service.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/service.yaml new file mode 100644 index 000000000..2dd698e83 --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "objectstorage-frontend.labels" . | nindent 4 }} + name: {{ include "objectstorage-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "objectstorage-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/values.yaml b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/values.yaml new file mode 100644 index 000000000..6f0e56bdc --- /dev/null +++ b/frontend/providers/objectstorage/deploy/charts/objectstorage-frontend/values.yaml @@ -0,0 +1,71 @@ +# Default values for objectstorage-frontend helm chart. + +image: ghcr.io/labring/sealos-objectstorage-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "object-storage-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +objectstorageConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + monitorUrl: "http://object-storage-monitor.objectstorage-system.svc.cluster.local:9090/q" + billingUrl: "" + billingSecret: "" + appLaunchpadUrl: "http://applaunchpad-frontend.applaunchpad-frontend.svc.cluster.local:3000/api/v1alpha" + appTokenJwtKey: "" + hostingPodCpuMilliCores: 200 + hostingPodMemoryMiB: 128 + hostingAppNamePrefix: "static-host" + hostingNetworkProtocol: "HTTP" + hostingNetworkPort: 80 + +resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 10m + memory: 128Mi + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - object-storage-frontend + topologyKey: "kubernetes.io/hostname" diff --git a/frontend/providers/objectstorage/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/objectstorage/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index 099ad341f..000000000 --- a/frontend/providers/objectstorage/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,128 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: objectstorage-frontend ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: object-storage-frontend - namespace: objectstorage-frontend - labels: - app: object-storage-frontend -spec: - replicas: 1 - revisionHistoryLimit: 1 - selector: - matchLabels: - app: object-storage-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 1 - maxSurge: 0 - template: - metadata: - labels: - app: object-storage-frontend - spec: - containers: - - name: object-storage-frontend - image: ghcr.io/labring/sealos-objectstorage-frontend:latest - ports: - - containerPort: 3000 - protocol: TCP - env: - - name: MONITOR_URL - value: http://object-storage-monitor.objectstorage-system.svc.cluster.local:9090/q - - name: APP_LAUNCHPAD_URL - value: http://applaunchpad-frontend.applaunchpad-frontend.svc.cluster.local:3000/api/v1alpha - resources: - limits: - cpu: 2000m - memory: 2048Mi - requests: - cpu: 10m - memory: 128Mi - imagePullPolicy: Always - volumeMounts: [] - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "app" - operator: In - values: - - object-storage-frontend - topologyKey: "kubernetes.io/hostname" - volumes: [] ---- -apiVersion: v1 -kind: Service -metadata: - name: object-storage-frontend - namespace: objectstorage-frontend - labels: - app: object-storage-frontend -spec: - ports: - - port: 3000 - targetPort: 3000 - protocol: TCP - selector: - app: object-storage-frontend ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/configuration-snippet: | - if ($request_uri ~* \.(js|css|gif|jpe?g|png)) { - expires 30d; - add_header Cache-Control "public"; - } - nginx.ingress.kubernetes.io/proxy-body-size: 3g - nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: '180' - nginx.ingress.kubernetes.io/proxy-send-timeout: '180' - name: object-storage-frontend - namespace: objectstorage-frontend -spec: - rules: - - host: objectstorage.{{ .cloudDomain }} - http: - paths: - - backend: - service: - name: object-storage-frontend - port: - number: 3000 - path: / - pathType: Prefix - tls: - - hosts: - - objectstorage.{{ .cloudDomain }} - secretName: wildcard-cert ---- -apiVersion: app.sealos.io/v1 -kind: App -metadata: - name: objectstorage - namespace: app-system -spec: - data: - desc: object storage - url: https://objectstorage.{{ .cloudDomain }}:{{ .cloudPort }} - displayType: normal - i18n: - zh: - name: 对象存储 - zh-Hans: - name: 对象存储 - icon: https://objectstorage.{{ .cloudDomain }}:{{ .cloudPort }}/logo.svg - menuData: - name: Object Storage - type: iframe \ No newline at end of file diff --git a/frontend/providers/objectstorage/deploy/object-storage-frontend-entrypoint.sh b/frontend/providers/objectstorage/deploy/object-storage-frontend-entrypoint.sh new file mode 100755 index 000000000..e92db886e --- /dev/null +++ b/frontend/providers/objectstorage/deploy/object-storage-frontend-entrypoint.sh @@ -0,0 +1,97 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"object-storage-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"objectstorage-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/objectstorage-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string objectstorageConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string objectstorageConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string objectstorageConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string objectstorageConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string objectstorageConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" +SEALOS_JWT_INTERNAL=${SEALOS_JWT_INTERNAL:-"${jwtInternal:-$(get_cm_value sealos-system sealos-config jwtInternal)}"} +add_set_string objectstorageConfig.appTokenJwtKey "${SEALOS_JWT_INTERNAL}" +add_set_string objectstorageConfig.monitorUrl "${monitorUrl:-}" +add_set_string objectstorageConfig.billingUrl "${billingUrl:-}" +add_set_string objectstorageConfig.billingSecret "${billingSecret:-}" +add_set_string objectstorageConfig.appLaunchpadUrl "${appLaunchpadUrl:-}" +add_set_string objectstorageConfig.hostingPodCpuMilliCores "${hostingPodCpuMilliCores:-}" +add_set_string objectstorageConfig.hostingPodMemoryMiB "${hostingPodMemoryMiB:-}" +add_set_string objectstorageConfig.hostingAppNamePrefix "${hostingAppNamePrefix:-}" +add_set_string objectstorageConfig.hostingNetworkProtocol "${hostingNetworkProtocol:-}" +add_set_string objectstorageConfig.hostingNetworkPort "${hostingNetworkPort:-}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap object-storage-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service object-storage-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment object-storage-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress object-storage-frontend +fi + +adopt_namespaced_resource app-system apps.app.sealos.io objectstorage + + +SERVICE_NAME="object-storage-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/objectstorage-frontend/object-storage-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/objectstorage-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS} diff --git a/frontend/providers/template/deploy/Kubefile b/frontend/providers/template/deploy/Kubefile index 150374fe3..0dffb2b52 100644 --- a/frontend/providers/template/deploy/Kubefile +++ b/frontend/providers/template/deploy/Kubefile @@ -3,17 +3,7 @@ FROM scratch USER 65532:65532 COPY registry registry -COPY manifests manifests +COPY charts charts +COPY template-frontend-entrypoint.sh template-frontend-entrypoint.sh -ENV cloudDomain="127.0.0.1.nip.io" -ENV cloudPort="" -ENV userDomain="" -ENV certSecretName="wildcard-cert" -ENV templateRepoUrl="https://github.com/labring-actions/templates" -ENV templateRepoBranch="main" -ENV templateRepoPath="templates" -ENV guideEnabled="true" -ENV billingUrl="http://account-service.account-system.svc:2333" -ENV enableReadmeFetch="true" - -CMD ["kubectl apply -f manifests"] \ No newline at end of file +CMD ["bash template-frontend-entrypoint.sh"] diff --git a/frontend/providers/template/deploy/charts/template-frontend/Chart.yaml b/frontend/providers/template/deploy/charts/template-frontend/Chart.yaml new file mode 100644 index 000000000..d4a3be130 --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: template-frontend +description: Sealos App Store Frontend Helm Chart +type: application +version: 1.0.0 +appVersion: "latest" diff --git a/frontend/providers/template/deploy/charts/template-frontend/template-frontend-values.yaml b/frontend/providers/template/deploy/charts/template-frontend/template-frontend-values.yaml new file mode 100644 index 000000000..fa47a7d93 --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/template-frontend-values.yaml @@ -0,0 +1,24 @@ +# Custom values for template-frontend helm chart. +# This file contains user-customizable configurations. + +replicaCount: 1 + +resources: + limits: + cpu: 2000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +templateConfig: + userDomain: "" + templateRepoUrl: "https://github.com/labring-actions/templates" + templateRepoBranch: "main" + templateRepoPath: "templates" + guideEnabled: true + billingUrl: "http://account-service.account-system.svc:2333" + enableReadmeFetch: true + brandName: "Sealos" + forcedLanguage: "en" + currencySymbol: "shellCoin" diff --git a/frontend/providers/template/deploy/charts/template-frontend/templates/_helpers.tpl b/frontend/providers/template/deploy/charts/template-frontend/templates/_helpers.tpl new file mode 100644 index 000000000..5443c0023 --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* Expand the name of the chart. */}} +{{- define "template-frontend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "template-frontend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- define "template-frontend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "template-frontend.labels" -}} +helm.sh/chart: {{ include "template-frontend.chart" . }} +{{ include "template-frontend.selectorLabels" . }} +{{ include "template-frontend.recommendedLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- define "template-frontend.selectorLabels" -}} +app: {{ include "template-frontend.fullname" . }} +{{- end }} + +{{- define "template-frontend.recommendedLabels" -}} +app.kubernetes.io/name: {{ include "template-frontend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "template-frontend.scheme" -}} +{{- if eq (toString .Values.templateConfig.disableHttps) "true" -}}http{{- else -}}https{{- end -}} +{{- end }} + +{{- define "template-frontend.port" -}} +{{- $scheme := include "template-frontend.scheme" . -}} +{{- $port := toString .Values.templateConfig.cloudPort -}} +{{- if eq $scheme "http" -}} +{{- $port = toString .Values.templateConfig.httpPort -}} +{{- end -}} +{{- if or (and (eq $scheme "https") (or (eq $port "") (eq $port "443"))) (and (eq $scheme "http") (or (eq $port "") (eq $port "80"))) -}} +{{- "" -}} +{{- else -}} +{{- $port -}} +{{- end }} +{{- end }} + +{{- define "template-frontend.portSuffix" -}} +{{- $port := include "template-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "template-frontend.portEnv" -}} +{{- $port := include "template-frontend.port" . -}} +{{- if $port -}}:{{ $port }}{{- end -}} +{{- end }} + +{{- define "template-frontend.cloudOrigin" -}} +{{- include "template-frontend.scheme" . -}}://{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} +{{- end }} + +{{- define "template-frontend.wildcardCloudOrigin" -}} +{{- include "template-frontend.scheme" . -}}://*.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} +{{- end }} + +{{- define "template-frontend.host" -}} +{{- default (printf "template.%s" .Values.templateConfig.cloudDomain) .Values.ingress.host -}} +{{- end }} + +{{- define "template-frontend.appUrl" -}} +{{- include "template-frontend.scheme" . -}}://{{ include "template-frontend.host" . }}{{ include "template-frontend.portSuffix" . }} +{{- end }} diff --git a/frontend/providers/template/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/template/deploy/charts/template-frontend/templates/app.yaml similarity index 53% rename from frontend/providers/template/deploy/manifests/appcr.yaml.tmpl rename to frontend/providers/template/deploy/charts/template-frontend/templates/app.yaml index bdcb5b988..30eb19b65 100644 --- a/frontend/providers/template/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/template/deploy/charts/template-frontend/templates/app.yaml @@ -5,14 +5,14 @@ metadata: namespace: app-system spec: data: - url: https://template.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} - displayType: normal + url: {{ include "template-frontend.appUrl" . | quote }} + icon: {{ printf "%s/logo.svg" (include "template-frontend.appUrl" .) | quote }} i18n: zh: name: 应用商店 zh-Hans: name: 应用商店 - icon: https://template.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg menuData: name: App Store - type: iframe \ No newline at end of file + type: iframe + displayType: normal diff --git a/frontend/providers/template/deploy/charts/template-frontend/templates/configmap.yaml b/frontend/providers/template/deploy/charts/template-frontend/templates/configmap.yaml new file mode 100644 index 000000000..c41fc84db --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/templates/configmap.yaml @@ -0,0 +1,86 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "template-frontend.fullname" . }}-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "template-frontend.labels" . | nindent 4 }} +data: + config.yaml: |- + # yaml-language-server: $schema=config.schema.json + cloud: + domain: {{ .Values.templateConfig.cloudDomain | quote }} + port: {{ default 443 .Values.templateConfig.cloudPort }} + regionUid: "" + certSecretName: {{ .Values.templateConfig.certSecretName | quote }} + + template: + ui: + brandName: {{ .Values.templateConfig.brandName | quote }} + forcedLanguage: {{ .Values.templateConfig.forcedLanguage | quote }} + currencySymbol: {{ .Values.templateConfig.currencySymbol | quote }} + meta: + canonicalUrl: https://template.example.org + customScripts: [] + carousel: + enabled: false + slides: [] + + repo: + url: {{ .Values.templateConfig.templateRepoUrl | quote }} + branch: {{ .Values.templateConfig.templateRepoBranch | quote }} + localDir: template + + features: + fetchReadme: {{ .Values.templateConfig.enableReadmeFetch }} + showAuthor: false + guide: {{ .Values.templateConfig.guideEnabled }} + + categories: + - slug: ai + i18n: + en: AI + zh: AI + - slug: backend + i18n: + en: Backend + zh: 后端 + - slug: blog + i18n: + en: Blog + zh: 博客 + - slug: database + i18n: + en: Database + zh: 数据库 + - slug: dev-ops + i18n: + en: DevOps + zh: 运维 + - slug: frontend + i18n: + en: Frontend + zh: 前端 + - slug: game + i18n: + en: Games + zh: 游戏 + - slug: low-code + i18n: + en: Low-Code + zh: 低代码 + - slug: monitor + i18n: + en: Monitoring + zh: 监控 + - slug: storage + i18n: + en: Storage + zh: 存储 + - slug: tool + i18n: + en: Tools + zh: 工具 + desktopDomain: {{ .Values.templateConfig.cloudDomain | quote }} + userDomain: {{ .Values.templateConfig.userDomain | quote }} + billingUrl: {{ .Values.templateConfig.billingUrl | quote }} diff --git a/frontend/providers/template/deploy/charts/template-frontend/templates/deployment.yaml b/frontend/providers/template/deploy/charts/template-frontend/templates/deployment.yaml new file mode 100644 index 000000000..e7299168f --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "template-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "template-frontend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "template-frontend.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + template: + metadata: + labels: + {{- include "template-frontend.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image | quote }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + {{- if .Values.securityContext }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + readinessProbe: + httpGet: + httpHeaders: + - name: Accept + value: application/json + path: /api/platform/getClientAppConfig + port: {{ .Values.service.port }} + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 3 + volumeMounts: + - name: template-frontend-volume + mountPath: /app/data/config.yaml + subPath: config.yaml + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: template-frontend-volume + configMap: + name: {{ include "template-frontend.fullname" . }}-config diff --git a/frontend/providers/template/deploy/charts/template-frontend/templates/ingress.yaml b/frontend/providers/template/deploy/charts/template-frontend/templates/ingress.yaml new file mode 100644 index 000000000..7aeb5a481 --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "template-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }} + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" + nginx.ingress.kubernetes.io/cors-allow-origin: "{{ include "template-frontend.cloudOrigin" . }}, {{ include "template-frontend.wildcardCloudOrigin" . }}" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/cors-max-age: "600" + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_clear_headers "X-Frame-Options:"; + more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "template-frontend.cloudOrigin" . }} {{ include "template-frontend.wildcardCloudOrigin" . }}"; + more_set_headers "X-Xss-Protection: 1; mode=block"; + higress.io/response-header-control-remove: X-Frame-Options + higress.io/response-header-control-update: | + Content-Security-Policy "default-src * blob: data: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }}; img-src * data: blob: resource: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} {{ .Values.templateConfig.cloudDomain }}{{ include "template-frontend.portSuffix" . }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' {{ include "template-frontend.cloudOrigin" . }} {{ include "template-frontend.wildcardCloudOrigin" . }}" + X-Xss-Protection "1; mode=block" + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: {{ include "template-frontend.host" . | quote }} + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: {{ include "template-frontend.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if not (eq (toString .Values.templateConfig.disableHttps) "true") }} + tls: + - hosts: + - {{ include "template-frontend.host" . | quote }} + secretName: {{ .Values.templateConfig.certSecretName | quote }} + {{- end }} diff --git a/frontend/providers/template/deploy/charts/template-frontend/templates/service.yaml b/frontend/providers/template/deploy/charts/template-frontend/templates/service.yaml new file mode 100644 index 000000000..d69fb29f7 --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + {{- include "template-frontend.labels" . | nindent 4 }} + name: {{ include "template-frontend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: {{ .Values.service.port }} + selector: + {{- include "template-frontend.selectorLabels" . | nindent 4 }} diff --git a/frontend/providers/template/deploy/charts/template-frontend/templates/template-static.yaml b/frontend/providers/template/deploy/charts/template-frontend/templates/template-static.yaml new file mode 100644 index 000000000..1aeabb748 --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/templates/template-static.yaml @@ -0,0 +1,52 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + namespace: {{ .Release.Namespace }} + name: template-static + labels: + {{- include "template-frontend.labels" . | nindent 4 }} +spec: + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 1 + schedule: "0 0 * * *" + jobTemplate: + spec: + template: + spec: + serviceAccountName: default + containers: + - name: template-static + image: bitnamilegacy/kubectl:latest + command: + - /bin/sh + - -c + - > + echo "$(kubectl get instances -A -o=jsonpath='{range .items[*]}{.spec.title}{"\n"}{end}' | tr a-z A-Z | sort | uniq -c | sort -nr | sed 's/ *//g')" > /tmp/install-count && + kubectl create configmap -n {{ .Release.Namespace }} template-static --from-file=/tmp/install-count -o yaml --dry-run=client | kubectl apply -f - + imagePullPolicy: IfNotPresent + restartPolicy: OnFailure +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: template-frontend-static-role +rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "get", "list", "watch", "update", "delete", "patch"] + - apiGroups: ["app.sealos.io"] + resources: ["instances"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: template-frontend-static-role-binding +subjects: + - kind: ServiceAccount + name: default + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: template-frontend-static-role + apiGroup: rbac.authorization.k8s.io diff --git a/frontend/providers/template/deploy/charts/template-frontend/values.yaml b/frontend/providers/template/deploy/charts/template-frontend/values.yaml new file mode 100644 index 000000000..60272a3a0 --- /dev/null +++ b/frontend/providers/template/deploy/charts/template-frontend/values.yaml @@ -0,0 +1,71 @@ +# Default values for template-frontend helm chart. + +image: ghcr.io/labring/sealos-template-frontend:latest +imagePullPolicy: Always + +imagePullSecrets: [] +fullnameOverride: "template-frontend" + +replicaCount: 1 + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + +securityContext: + runAsNonRoot: true + runAsUser: 1001 + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + +service: + type: ClusterIP + port: 3000 + +ingress: + className: nginx + host: "" + annotations: {} + +templateConfig: + cloudDomain: "127.0.0.1.nip.io" + cloudPort: "" + httpPort: "" + disableHttps: false + certSecretName: "wildcard-cert" + userDomain: "" + templateRepoUrl: "https://github.com/labring-actions/templates" + templateRepoBranch: "main" + templateRepoPath: "templates" + guideEnabled: true + billingUrl: "http://account-service.account-system.svc:2333" + enableReadmeFetch: true + brandName: "Sealos" + forcedLanguage: "en" + currencySymbol: "shellCoin" + +resources: + limits: + cpu: 2000m + memory: 1024Mi + requests: + cpu: 10m + memory: 128Mi + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - template-frontend + topologyKey: "kubernetes.io/hostname" diff --git a/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl deleted file mode 100644 index a5c171544..000000000 --- a/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl +++ /dev/null @@ -1,229 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: template-frontend - name: template-frontend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: template-frontend-config - namespace: template-frontend -data: - config.yaml: |- - # yaml-language-server: $schema=config.schema.json - cloud: - domain: '{{ .cloudDomain }}' - port: {{ .cloudPort }} - regionUid: '' - certSecretName: '{{ .certSecretName }}' - - template: - ui: - brandName: Sealos - forcedLanguage: en - currencySymbol: shellCoin - meta: - canonicalUrl: https://template.example.org - customScripts: [] - carousel: - enabled: false - slides: [] - - repo: - url: {{ .templateRepoUrl }} - branch: {{ .templateRepoBranch }} - localDir: template - - features: - fetchReadme: {{ .enableReadmeFetch }} - showAuthor: false - guide: {{ .guideEnabled }} - - # cdnHost: cdn.jsdelivr.net - categories: - - slug: ai - i18n: - en: AI - zh: AI - - slug: backend - i18n: - en: Backend - zh: 后端 - - slug: blog - i18n: - en: Blog - zh: 博客 - - slug: database - i18n: - en: Database - zh: 数据库 - - slug: dev-ops - i18n: - en: DevOps - zh: 运维 - - slug: frontend - i18n: - en: Frontend - zh: 前端 - - slug: game - i18n: - en: Games - zh: 游戏 - - slug: low-code - i18n: - en: Low-Code - zh: 低代码 - - slug: monitor - i18n: - en: Monitoring - zh: 监控 - - slug: storage - i18n: - en: Storage - zh: 存储 - - slug: tool - i18n: - en: Tools - zh: 工具 - desktopDomain: '{{ .cloudDomain }}' - userDomain: '{{ .userDomain }}' - billingUrl: '{{ .billingUrl }}' - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: template-frontend - namespace: template-frontend -spec: - selector: - matchLabels: - app: template-frontend - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 25% - maxSurge: 25% - template: - metadata: - labels: - app: template-frontend - spec: - containers: - - name: template-frontend - resources: - limits: - cpu: 2000m - memory: 1024Mi - requests: - cpu: 10m - memory: 128Mi - securityContext: - runAsNonRoot: true - runAsUser: 1001 - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - image: ghcr.io/labring/sealos-template-frontend:latest - imagePullPolicy: Always - readinessProbe: - httpGet: - httpHeaders: - - name: Accept - value: application/json - path: /api/platform/getClientAppConfig - port: 3000 - scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 3 - - volumeMounts: - - name: template-frontend-volume - mountPath: /app/data/config.yaml - subPath: config.yaml - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "app" - operator: In - values: - - template-frontend - topologyKey: "kubernetes.io/hostname" - volumes: - - name: template-frontend-volume - configMap: - name: template-frontend-config ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: template-frontend - name: template-frontend - namespace: template-frontend -spec: - ports: - - name: http - port: 3000 - protocol: TCP - targetPort: 3000 - selector: - app: template-frontend ---- -apiVersion: batch/v1 -kind: CronJob -metadata: - namespace: template-frontend - name: template-static -spec: - successfulJobsHistoryLimit: 3 - failedJobsHistoryLimit: 1 - schedule: "0 0 * * *" - jobTemplate: - spec: - template: - spec: - serviceAccountName: default - containers: - - name: template-static - image: bitnamilegacy/kubectl:latest - command: - - /bin/sh - - -c - - > - echo "$(kubectl get instances -A -o=jsonpath='{range .items[*]}{.spec.title}{"\n"}{end}' | tr a-z A-Z | sort | uniq -c | sort -nr | sed 's/ *//g')" > /tmp/install-count && - kubectl create configmap -n template-frontend template-static --from-file=/tmp/install-count -o yaml --dry-run=client | kubectl apply -f - - imagePullPolicy: IfNotPresent - restartPolicy: OnFailure ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: template-frontend-static-role -rules: -- apiGroups: [""] - resources: ["configmaps"] - verbs: ["create", "get", "list", "watch", "update", "delete","patch"] -- apiGroups: ["app.sealos.io"] - resources: ["instances"] - verbs: ["get", "list", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: template-frontend-static-role-binding -subjects: -- kind: ServiceAccount - name: default - namespace: template-frontend -roleRef: - kind: ClusterRole - name: template-frontend-static-role - apiGroup: rbac.authorization.k8s.io diff --git a/frontend/providers/template/deploy/manifests/ingress.yaml.tmpl b/frontend/providers/template/deploy/manifests/ingress.yaml.tmpl deleted file mode 100644 index 07417adff..000000000 --- a/frontend/providers/template/deploy/manifests/ingress.yaml.tmpl +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/enable-cors: "true" - nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, DELETE, PATCH, OPTIONS" - nginx.ingress.kubernetes.io/cors-allow-origin: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}, https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - nginx.ingress.kubernetes.io/cors-allow-credentials: "true" - nginx.ingress.kubernetes.io/cors-max-age: "600" - nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - nginx.ingress.kubernetes.io/configuration-snippet: | - more_clear_headers "X-Frame-Options:"; - more_set_headers "Content-Security-Policy: default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}"; - more_set_headers "X-Xss-Protection: 1; mode=block"; - higress.io/response-header-control-remove: X-Frame-Options - higress.io/response-header-control-update: | - Content-Security-Policy "default-src * blob: data: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; img-src * data: blob: resource: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}; connect-src * wss: blob: resource:; style-src 'self' 'unsafe-inline' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} resource: *.baidu.com *.bdstatic.com https://js.stripe.com; frame-src 'self' *.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} {{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} mailto: tel: weixin: mtt: *.baidu.com https://js.stripe.com; frame-ancestors 'self' https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }} https://*.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}" - X-Xss-Protection "1; mode=block" - name: template-frontend - namespace: template-frontend -spec: - rules: - - host: template.{{ .cloudDomain }} - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: template-frontend - port: - number: 3000 - tls: - - hosts: - - template.{{ .cloudDomain }} - secretName: wildcard-cert \ No newline at end of file diff --git a/frontend/providers/template/deploy/template-frontend-entrypoint.sh b/frontend/providers/template/deploy/template-frontend-entrypoint.sh new file mode 100755 index 000000000..7b9186441 --- /dev/null +++ b/frontend/providers/template/deploy/template-frontend-entrypoint.sh @@ -0,0 +1,98 @@ +#!/bin/bash +set -e + +RELEASE_NAME=${RELEASE_NAME:-"template-frontend"} +RELEASE_NAMESPACE=${RELEASE_NAMESPACE:-"template-frontend"} +CHART_PATH=${CHART_PATH:-"./charts/template-frontend"} +HELM_OPTS=${HELM_OPTS:-""} +HELM_OPTIONS=${HELM_OPTIONS:-""} +AUTO_CONFIG_HELM_OPTS="" + +get_cm_value() { + local namespace="$1" + local name="$2" + local key="$3" + kubectl get configmap "${name}" -n "${namespace}" -o "jsonpath={.data.${key}}" 2>/dev/null || true +} + +add_set_string() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + AUTO_CONFIG_HELM_OPTS="${AUTO_CONFIG_HELM_OPTS} --set-string ${key}=${value}" + fi +} + +SEALOS_CLOUD_DOMAIN=${SEALOS_CLOUD_DOMAIN:-"${cloudDomain:-$(get_cm_value sealos-system sealos-config cloudDomain)}"} +add_set_string templateConfig.cloudDomain "${SEALOS_CLOUD_DOMAIN}" +SEALOS_CLOUD_PORT=${SEALOS_CLOUD_PORT:-"${cloudPort:-$(get_cm_value sealos-system sealos-config cloudPort)}"} +add_set_string templateConfig.cloudPort "${SEALOS_CLOUD_PORT}" +SEALOS_HTTP_PORT=${SEALOS_HTTP_PORT:-"${httpPort:-$(get_cm_value sealos-system sealos-config httpPort)}"} +add_set_string templateConfig.httpPort "${SEALOS_HTTP_PORT}" +SEALOS_DISABLE_HTTPS=${SEALOS_DISABLE_HTTPS:-"${disableHttps:-$(get_cm_value sealos-system sealos-config disableHttps)}"} +add_set_string templateConfig.disableHttps "${SEALOS_DISABLE_HTTPS}" +SEALOS_CERT_SECRET_NAME=${SEALOS_CERT_SECRET_NAME:-"${certSecretName:-$(get_cm_value sealos-system sealos-config certSecretName)}"} +add_set_string templateConfig.certSecretName "${SEALOS_CERT_SECRET_NAME}" +add_set_string templateConfig.userDomain "${userDomain:-}" +add_set_string templateConfig.templateRepoUrl "${templateRepoUrl:-}" +add_set_string templateConfig.templateRepoBranch "${templateRepoBranch:-}" +add_set_string templateConfig.templateRepoPath "${templateRepoPath:-}" +add_set_string templateConfig.guideEnabled "${guideEnabled:-}" +add_set_string templateConfig.billingUrl "${billingUrl:-}" +add_set_string templateConfig.enableReadmeFetch "${enableReadmeFetch:-}" +add_set_string templateConfig.brandName "${brandName:-}" +add_set_string templateConfig.forcedLanguage "${forcedLanguage:-}" +add_set_string templateConfig.currencySymbol "${currencySymbol:-}" + +adopt_namespaced_resource() { + local namespace="$1" + local kind="$2" + local name="$3" + if kubectl -n "${namespace}" get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${namespace}/${name}..." + kubectl -n "${namespace}" label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl -n "${namespace}" annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +adopt_cluster_resource() { + local kind="$1" + local name="$2" + if kubectl get "${kind}" "${name}" >/dev/null 2>&1; then + echo "Adopting ${kind} ${name}..." + kubectl label "${kind}" "${name}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate "${kind}" "${name}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + fi +} + +echo "Checking and adopting existing resources..." +if kubectl get namespace "${RELEASE_NAMESPACE}" >/dev/null 2>&1; then + kubectl label namespace "${RELEASE_NAMESPACE}" app.kubernetes.io/managed-by=Helm --overwrite >/dev/null 2>&1 || true + kubectl annotate namespace "${RELEASE_NAMESPACE}" meta.helm.sh/release-name="${RELEASE_NAME}" meta.helm.sh/release-namespace="${RELEASE_NAMESPACE}" --overwrite >/dev/null 2>&1 || true + + adopt_namespaced_resource "${RELEASE_NAMESPACE}" configmap template-frontend-config + adopt_namespaced_resource "${RELEASE_NAMESPACE}" service template-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" deployment template-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" ingress template-frontend + adopt_namespaced_resource "${RELEASE_NAMESPACE}" cronjob template-static +fi + +adopt_namespaced_resource app-system apps.app.sealos.io template +adopt_cluster_resource clusterrole template-frontend-static-role +adopt_cluster_resource clusterrolebinding template-frontend-static-role-binding + +SERVICE_NAME="template-frontend" +USER_VALUES_PATH="/root/.sealos/cloud/values/core/${SERVICE_NAME}-values.yaml" + +if [ ! -f "${USER_VALUES_PATH}" ]; then + mkdir -p "$(dirname "${USER_VALUES_PATH}")" + cp "./charts/template-frontend/template-frontend-values.yaml" "${USER_VALUES_PATH}" +fi + +HELM_ARGS="${AUTO_CONFIG_HELM_OPTS} ${HELM_OPTIONS} ${HELM_OPTS}" + +echo "Deploying Helm chart..." +helm upgrade -i "${RELEASE_NAME}" -n "${RELEASE_NAMESPACE}" --create-namespace "${CHART_PATH}" \ + -f "./charts/template-frontend/values.yaml" \ + -f "${USER_VALUES_PATH}" \ + ${HELM_ARGS}