[codex] Add digest-safe k3s update-center assets (#326)
* Add digest-safe k3s update-center assets * Harden k3s update-center assets * Trim k3s env secret names safely
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: metapi
|
||||||
|
description: Minimal metapi chart for digest-aware k3s update-center setups
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "1.2.3"
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{{- define "metapi.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- define "metapi.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride -}}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- $name := include "metapi.name" . -}}
|
||||||
|
{{- if contains $name .Release.Name -}}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- define "metapi.envSecretName" -}}
|
||||||
|
{{- printf "%s-env" (include "metapi.fullname" .) | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- define "metapi.labels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "metapi.name" . }}
|
||||||
|
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end -}}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{{- if and .Values.persistence.hostPath (gt (int .Values.replicaCount) 1) -}}
|
||||||
|
{{- fail "deploy/k3s/chart uses node-local hostPath storage; keep replicaCount at 1 or switch persistence.hostPath to shared storage before scaling" -}}
|
||||||
|
{{- end }}
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "metapi.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "metapi.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
revisionHistoryLimit: 2
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: {{ .Values.deployment.strategy.maxSurge }}
|
||||||
|
maxUnavailable: {{ .Values.deployment.strategy.maxUnavailable }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: {{ include "metapi.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: {{ include "metapi.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
annotations:
|
||||||
|
metapi/image-tag: {{ .Values.image.tag | quote }}
|
||||||
|
metapi/image-digest: {{ .Values.image.digest | quote }}
|
||||||
|
checksum/env-secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum | quote }}
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: metapi
|
||||||
|
image: "{{ .Values.image.repository }}{{ if .Values.image.digest }}@{{ .Values.image.digest }}{{ else }}:{{ .Values.image.tag }}{{ end }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: {{ include "metapi.envSecretName" . }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 4000
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 20
|
||||||
|
volumeMounts:
|
||||||
|
- name: metapi-data
|
||||||
|
mountPath: /app/data
|
||||||
|
volumes:
|
||||||
|
- name: metapi-data
|
||||||
|
hostPath:
|
||||||
|
path: {{ .Values.persistence.hostPath }}
|
||||||
|
type: DirectoryOrCreate
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "metapi.envSecretName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "metapi.labels" . | nindent 4 }}
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
AUTH_TOKEN: {{ required "env.authToken is required" .Values.env.authToken | quote }}
|
||||||
|
PROXY_TOKEN: {{ required "env.proxyToken is required" .Values.env.proxyToken | quote }}
|
||||||
|
DB_TYPE: {{ .Values.env.dbType | quote }}
|
||||||
|
{{- if eq (lower (toString .Values.env.dbType)) "sqlite" }}
|
||||||
|
DB_URL: {{ default "./data/hub.db" .Values.env.dbUrl | quote }}
|
||||||
|
{{- else }}
|
||||||
|
DB_URL: {{ required "env.dbUrl is required when env.dbType is mysql/postgres" .Values.env.dbUrl | quote }}
|
||||||
|
{{- end }}
|
||||||
|
DB_SSL: {{ .Values.env.dbSsl | quote }}
|
||||||
|
DATA_DIR: {{ .Values.env.dataDir | quote }}
|
||||||
|
PORT: {{ .Values.env.port | quote }}
|
||||||
|
TZ: {{ .Values.env.tz | quote }}
|
||||||
|
CHECKIN_CRON: {{ .Values.env.checkinCron | quote }}
|
||||||
|
BALANCE_REFRESH_CRON: {{ .Values.env.balanceRefreshCron | quote }}
|
||||||
|
DEPLOY_HELPER_TOKEN: {{ .Values.env.deployHelperToken | quote }}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "metapi.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "metapi.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: {{ include "metapi.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: {{ .Values.service.port }}
|
||||||
|
targetPort: http
|
||||||
|
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) .Values.service.nodePort }}
|
||||||
|
nodePort: {{ .Values.service.nodePort }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: 1467078763/metapi
|
||||||
|
tag: latest
|
||||||
|
digest: ""
|
||||||
|
pullPolicy: Always
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: NodePort
|
||||||
|
port: 4000
|
||||||
|
nodePort: 30400
|
||||||
|
|
||||||
|
deployment:
|
||||||
|
strategy:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
hostPath: /opt/1panel/docker/compose/metapihub/data
|
||||||
|
|
||||||
|
env:
|
||||||
|
authToken: ""
|
||||||
|
proxyToken: ""
|
||||||
|
dbType: postgres
|
||||||
|
dbUrl: ""
|
||||||
|
dbSsl: "false"
|
||||||
|
dataDir: /app/data
|
||||||
|
port: "4000"
|
||||||
|
tz: Asia/Shanghai
|
||||||
|
checkinCron: "0 8 * * *"
|
||||||
|
balanceRefreshCron: "0 * * * *"
|
||||||
|
deployHelperToken: ""
|
||||||
@@ -47,7 +47,33 @@ spec:
|
|||||||
- name: helper
|
- name: helper
|
||||||
image: 1467078763/metapi:latest
|
image: 1467078763/metapi:latest
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
command: ["npm", "run", "start:deploy-helper"]
|
command: ["/bin/sh", "-lc"]
|
||||||
|
args:
|
||||||
|
- |
|
||||||
|
TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
|
||||||
|
NAMESPACE="$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)"
|
||||||
|
cat >/tmp/kubeconfig <<EOF
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Config
|
||||||
|
clusters:
|
||||||
|
- name: in-cluster
|
||||||
|
cluster:
|
||||||
|
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
contexts:
|
||||||
|
- name: in-cluster
|
||||||
|
context:
|
||||||
|
cluster: in-cluster
|
||||||
|
namespace: ${NAMESPACE}
|
||||||
|
user: helper
|
||||||
|
current-context: in-cluster
|
||||||
|
users:
|
||||||
|
- name: helper
|
||||||
|
user:
|
||||||
|
token: ${TOKEN}
|
||||||
|
EOF
|
||||||
|
export KUBECONFIG=/tmp/kubeconfig
|
||||||
|
exec npm run start:deploy-helper
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 9850
|
- containerPort: 9850
|
||||||
name: http
|
name: http
|
||||||
@@ -73,6 +99,15 @@ spec:
|
|||||||
port: http
|
port: http
|
||||||
initialDelaySeconds: 15
|
initialDelaySeconds: 15
|
||||||
periodSeconds: 20
|
periodSeconds: 20
|
||||||
|
volumeMounts:
|
||||||
|
- name: metapi-chart
|
||||||
|
mountPath: /opt/metapi-k3s/chart
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: metapi-chart
|
||||||
|
hostPath:
|
||||||
|
path: /opt/metapi-k3s/chart
|
||||||
|
type: Directory
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ Deploy Helper 是一个跑在集群里的小服务。它不负责对外提供 Me
|
|||||||
仓库里带了一个最小示例清单:
|
仓库里带了一个最小示例清单:
|
||||||
|
|
||||||
- `deploy/k3s/metapi-deploy-helper.yaml`
|
- `deploy/k3s/metapi-deploy-helper.yaml`
|
||||||
|
- `deploy/k3s/chart`
|
||||||
|
|
||||||
使用前至少要改这些值:
|
使用前至少要改这些值:
|
||||||
|
|
||||||
@@ -177,13 +178,67 @@ Deploy Helper 是一个跑在集群里的小服务。它不负责对外提供 Me
|
|||||||
- `DEPLOY_HELPER_TOKEN`
|
- `DEPLOY_HELPER_TOKEN`
|
||||||
- `image`
|
- `image`
|
||||||
|
|
||||||
|
如果你准备直接按仓库示例接入,推荐把它们放到这两个路径:
|
||||||
|
|
||||||
|
- `/opt/metapi-k3s/metapi-deploy-helper.yaml`
|
||||||
|
- `/opt/metapi-k3s/chart`
|
||||||
|
|
||||||
|
这里有一个很容易漏掉、但会直接决定更新中心是否真的可靠的约束:
|
||||||
|
|
||||||
|
- 后台里的 `Chart Ref`
|
||||||
|
- helper Pod 里挂载进去的 chart 路径
|
||||||
|
|
||||||
|
这两个值必须指向同一份 chart。
|
||||||
|
|
||||||
|
如果你在设置页把 `Chart Ref` 填成 `/opt/metapi-k3s/chart`,但 helper Pod 里根本没有把那份 chart 挂进去,那么更新中心即使能看到版本,也无法稳定把 digest 部署到真正运行中的 Pod。
|
||||||
|
|
||||||
最小部署命令示例:
|
最小部署命令示例:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl create namespace ai
|
kubectl create namespace ai
|
||||||
kubectl apply -f deploy/k3s/metapi-deploy-helper.yaml
|
mkdir -p /opt/metapi-k3s/chart
|
||||||
|
cp -R deploy/k3s/chart/. /opt/metapi-k3s/chart/
|
||||||
|
cp deploy/k3s/metapi-deploy-helper.yaml /opt/metapi-k3s/metapi-deploy-helper.yaml
|
||||||
|
kubectl apply -f /opt/metapi-k3s/metapi-deploy-helper.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 1.1 首次接入前一定要做的自检
|
||||||
|
|
||||||
|
新用户最容易踩的坑不是 helper 起不来,而是:
|
||||||
|
|
||||||
|
- chart 虽然能执行 `helm upgrade`
|
||||||
|
- 但它其实没有消费 `image.digest`
|
||||||
|
- 同时 `imagePullPolicy` 还停留在 `IfNotPresent`
|
||||||
|
|
||||||
|
这样更新中心会显示任务成功、Helm revision 也会增加,但线上真正运行的镜像可能根本没变。
|
||||||
|
|
||||||
|
仓库自带的 `deploy/k3s/chart` 已经把这件事处理成默认正确:
|
||||||
|
|
||||||
|
- `values.yaml` 里包含 `image.digest`
|
||||||
|
- `templates/deployment.yaml` 在有 digest 时会渲染成 `repository@digest`
|
||||||
|
- chart 默认 `imagePullPolicy: Always`
|
||||||
|
- helper 示例清单也默认 `imagePullPolicy: Always`
|
||||||
|
|
||||||
|
你可以在接入前先跑一次自检:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm template metapi /opt/metapi-k3s/chart \
|
||||||
|
--set image.repository=1467078763/metapi \
|
||||||
|
--set image.tag=latest \
|
||||||
|
--set-string image.digest=sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef \
|
||||||
|
| grep -n 'image:'
|
||||||
|
```
|
||||||
|
|
||||||
|
你期望看到的是:
|
||||||
|
|
||||||
|
- `image: "1467078763/metapi@sha256:..."`
|
||||||
|
|
||||||
|
如果输出仍然是:
|
||||||
|
|
||||||
|
- `image: "1467078763/metapi:latest"`
|
||||||
|
|
||||||
|
那说明这份 chart 还是 tag 语义,先不要继续配置更新中心。
|
||||||
|
|
||||||
### 2. 让主 Metapi 和 helper 用同一个 token
|
### 2. 让主 Metapi 和 helper 用同一个 token
|
||||||
|
|
||||||
主 Metapi 端支持这两个环境变量,设置一个即可:
|
主 Metapi 端支持这两个环境变量,设置一个即可:
|
||||||
@@ -206,7 +261,7 @@ helper 端使用:
|
|||||||
| `Deploy Helper URL` | 主 Metapi 访问 helper 的地址 | `http://metapi-deploy-helper.ai.svc.cluster.local:9850` |
|
| `Deploy Helper URL` | 主 Metapi 访问 helper 的地址 | `http://metapi-deploy-helper.ai.svc.cluster.local:9850` |
|
||||||
| `Namespace` | 目标 release 所在命名空间 | `ai` |
|
| `Namespace` | 目标 release 所在命名空间 | `ai` |
|
||||||
| `Release Name` | Helm release 名 | `metapi` |
|
| `Release Name` | Helm release 名 | `metapi` |
|
||||||
| `Chart Ref` | 你的 Helm chart 引用 | `oci://ghcr.io/cita-777/charts/metapi` |
|
| `Chart Ref` | helper Pod 能访问到的 chart 引用 | `/opt/metapi-k3s/chart` |
|
||||||
| `Image Repository` | 升级时写入 chart 的镜像仓库 | `1467078763/metapi` |
|
| `Image Repository` | 升级时写入 chart 的镜像仓库 | `1467078763/metapi` |
|
||||||
| `默认部署来源` | 默认从 GitHub 还是 Docker Hub 取版本 | `GitHub Releases` |
|
| `默认部署来源` | 默认从 GitHub 还是 Docker Hub 取版本 | `GitHub Releases` |
|
||||||
|
|
||||||
@@ -233,6 +288,17 @@ helper 端使用:
|
|||||||
|
|
||||||
如果实时日志流断开,页面会自动回退到最近任务快照。
|
如果实时日志流断开,页面会自动回退到最近任务快照。
|
||||||
|
|
||||||
|
### 4.1 新用户最值得先做的一次确认
|
||||||
|
|
||||||
|
第一次接入时,不要急着点部署。先确认下面 4 件事同时成立:
|
||||||
|
|
||||||
|
1. `Chart Ref` 和 helper 挂载路径指向的是同一份 chart。
|
||||||
|
2. 这份 chart 支持 `image.digest`,并且在有 digest 时会渲染成 `repository@digest`。
|
||||||
|
3. chart 和 helper 都没有继续使用会掩盖新镜像的 `imagePullPolicy: IfNotPresent`。
|
||||||
|
4. 页面里显示的目标 digest,和 helper 最终读到的运行中 digest 能对上。
|
||||||
|
|
||||||
|
只要这 4 件事成立,新用户就不会再遇到“页面说部署成功,但线上还是旧镜像”的假成功。
|
||||||
|
|
||||||
## 这套能力当前不适合什么场景
|
## 这套能力当前不适合什么场景
|
||||||
|
|
||||||
- 只有一台 Docker 主机、一个 Compose 文件、一个 Metapi 容器
|
- 只有一台 Docker 主机、一个 Compose 文件、一个 Metapi 容器
|
||||||
@@ -268,6 +334,22 @@ About 页里的“更新提醒”更像一个轻量入口:
|
|||||||
|
|
||||||
缺其中任何一项,都只能看,不能真正部署。
|
缺其中任何一项,都只能看,不能真正部署。
|
||||||
|
|
||||||
|
### 为什么页面显示任务成功,但线上镜像还是没变
|
||||||
|
|
||||||
|
最常见的根因只有两类:
|
||||||
|
|
||||||
|
- 你的 chart 其实没有消费 `image.digest`
|
||||||
|
- 你的 Deployment 或 helper 还在使用 `imagePullPolicy: IfNotPresent`
|
||||||
|
|
||||||
|
这两种情况叠在一起时,就会出现“Helm revision 往前走了,但实际运行的 imageID 没动”的假成功。
|
||||||
|
|
||||||
|
如果你是第一次接入,最稳妥的方式就是直接从仓库自带的:
|
||||||
|
|
||||||
|
- `deploy/k3s/chart`
|
||||||
|
- `deploy/k3s/metapi-deploy-helper.yaml`
|
||||||
|
|
||||||
|
开始,不要自己先抄一份简化版再改。
|
||||||
|
|
||||||
### 为什么我只有 Docker Compose,按钮没有意义
|
### 为什么我只有 Docker Compose,按钮没有意义
|
||||||
|
|
||||||
因为当前部署链路不是去操作外部 Docker 主机,而是去调用集群里的 helper,然后由 helper 执行 `helm` / `kubectl`。
|
因为当前部署链路不是去操作外部 Docker 主机,而是去调用集群里的 helper,然后由 helper 执行 `helm` / `kubectl`。
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { existsSync, readFileSync } from 'node:fs';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
|
function readRepoFile(relativePath: string) {
|
||||||
|
return readFileSync(resolve(process.cwd(), relativePath), 'utf8').replace(/\r\n/g, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeWhitespace(value: string) {
|
||||||
|
return value.replace(/\s+/g, ' ').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('k3s deploy assets', () => {
|
||||||
|
it('ships a digest-aware example chart for update-center users', () => {
|
||||||
|
const requiredFiles = [
|
||||||
|
'deploy/k3s/chart/Chart.yaml',
|
||||||
|
'deploy/k3s/chart/values.yaml',
|
||||||
|
'deploy/k3s/chart/templates/_helpers.tpl',
|
||||||
|
'deploy/k3s/chart/templates/deployment.yaml',
|
||||||
|
'deploy/k3s/chart/templates/secret.yaml',
|
||||||
|
'deploy/k3s/chart/templates/service.yaml',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const filePath of requiredFiles) {
|
||||||
|
expect(existsSync(resolve(process.cwd(), filePath)), filePath).toBe(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = readRepoFile('deploy/k3s/chart/values.yaml');
|
||||||
|
const deploymentTemplate = readRepoFile('deploy/k3s/chart/templates/deployment.yaml');
|
||||||
|
const serviceTemplate = readRepoFile('deploy/k3s/chart/templates/service.yaml');
|
||||||
|
const helpersTemplate = readRepoFile('deploy/k3s/chart/templates/_helpers.tpl');
|
||||||
|
const normalizedDeploymentTemplate = normalizeWhitespace(deploymentTemplate);
|
||||||
|
const normalizedServiceTemplate = normalizeWhitespace(serviceTemplate);
|
||||||
|
|
||||||
|
expect(values).toContain('digest:');
|
||||||
|
expect(values).toContain('pullPolicy: Always');
|
||||||
|
expect(deploymentTemplate).toMatch(/\.Values\.image\.digest/);
|
||||||
|
expect(normalizedDeploymentTemplate).toContain('{{ if .Values.image.digest }}@{{ .Values.image.digest }}{{ else }}:{{ .Values.image.tag }}{{ end }}');
|
||||||
|
expect(deploymentTemplate).toMatch(/metapi\/image-digest:\s*\{\{\s*\.Values\.image\.digest\s*\|\s*quote\s*\}\}/);
|
||||||
|
expect(deploymentTemplate).toMatch(/checksum\/env-secret:/);
|
||||||
|
expect(deploymentTemplate).toMatch(/replicaCount at 1 or switch persistence\.hostPath to shared storage before scaling/);
|
||||||
|
expect(normalizedServiceTemplate).toContain('{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) .Values.service.nodePort }}');
|
||||||
|
expect(helpersTemplate).toContain('define "metapi.envSecretName"');
|
||||||
|
expect(helpersTemplate).toContain('printf "%s-env"');
|
||||||
|
expect(deploymentTemplate).toContain('include "metapi.envSecretName"');
|
||||||
|
expect(readRepoFile('deploy/k3s/chart/templates/secret.yaml')).toContain('include "metapi.envSecretName"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the helper manifest on a pull policy that can pick up fresh latest tags', () => {
|
||||||
|
const helperManifest = readRepoFile('deploy/k3s/metapi-deploy-helper.yaml');
|
||||||
|
|
||||||
|
expect(helperManifest).toContain('imagePullPolicy: Always');
|
||||||
|
expect(helperManifest).toContain('mountPath: /opt/metapi-k3s/chart');
|
||||||
|
expect(helperManifest).toContain('readOnly: true');
|
||||||
|
expect(helperManifest).toContain('path: /opt/metapi-k3s/chart');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('documents the local chart path and digest requirement for new k3s users', () => {
|
||||||
|
const docs = readRepoFile('docs/k3s-update-center.md');
|
||||||
|
|
||||||
|
expect(docs).toContain('deploy/k3s/chart');
|
||||||
|
expect(docs).toContain('/opt/metapi-k3s/chart');
|
||||||
|
expect(docs).toContain('image.digest');
|
||||||
|
expect(docs).toContain('imagePullPolicy: Always');
|
||||||
|
expect(docs).toContain('repository@digest');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user