diff --git a/charts/aisix-cp/Chart.yaml b/charts/aisix-cp/Chart.yaml index 75aef71..074838b 100644 --- a/charts/aisix-cp/Chart.yaml +++ b/charts/aisix-cp/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: aisix-cp description: Helm chart for AISIX control plane (cp-api, dp-manager, dashboard) type: application -version: 1.7.0 -appVersion: "1.7.0" +version: 1.7.1 +appVersion: "1.7.1" maintainers: - name: API7 diff --git a/charts/aisix-cp/README.md b/charts/aisix-cp/README.md index b74e381..b92c4d0 100644 --- a/charts/aisix-cp/README.md +++ b/charts/aisix-cp/README.md @@ -1,6 +1,6 @@ # aisix-cp -![Version: 1.7.0](https://img.shields.io/badge/Version-1.7.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.0](https://img.shields.io/badge/AppVersion-1.7.0-informational?style=flat-square) +![Version: 1.7.1](https://img.shields.io/badge/Version-1.7.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.1](https://img.shields.io/badge/AppVersion-1.7.1-informational?style=flat-square) Helm chart for AISIX control plane (cp-api, dp-manager, dashboard) @@ -102,7 +102,7 @@ helm repo add api7 https://charts.api7.ai helm repo update helm install aisix-cp api7/aisix-cp --namespace aisix --create-namespace \ - --version 1.7.0 \ + --version 1.7.1 \ -f cp-values.yaml ``` @@ -111,7 +111,7 @@ same install, as long as you record what you generated: ```sh helm install aisix-cp api7/aisix-cp --namespace aisix --create-namespace \ - --version 1.7.0 \ + --version 1.7.1 \ --set secrets.masterKey="$(openssl rand -base64 32)" \ --set postgresql.auth.postgresPassword="$(openssl rand -hex 24)" \ --set postgresql.auth.password="$(openssl rand -hex 24)" @@ -181,7 +181,7 @@ fails the handshake with nothing logged to say why. The console's generated commands carry a gateway image, taken from `api.dpImage`. Left empty — the default — `cp-api` hands out the gateway of its -own release, so this release hands out `docker.io/api7/aisix:1.7.0`. +own release, so this release hands out `docker.io/api7/aisix:1.7.1`. Set it only to pin a different image, and remember that a value set explicitly carries forward across upgrades, so gateways added later come up on the old image. @@ -343,7 +343,7 @@ install time: ```sh helm install aisix-cp api7/aisix-cp --namespace aisix --create-namespace \ - --version 1.7.0 \ + --version 1.7.1 \ -f cp-values.yaml \ --set postgresql.primary.podSecurityContext.enabled=false \ --set postgresql.primary.containerSecurityContext.enabled=false @@ -407,7 +407,7 @@ plane and hands out the gateway image from the same release as the chart. | api.bootstrapAdmin.existingSecretKey | string | `"bootstrap-admin-password"` | Key inside bootstrapAdmin.existingSecret holding the password | | api.bootstrapAdmin.password | string | `""` | Initial password (12 to 128 characters), stored in the chart's Secret. Ignored when existingSecret is set. Empty generates one (see above) | | api.corsAllowedOrigins | list | `[]` | Browser origins allowed to call cp-api cross-origin, as bare origins (scheme://host[:port]). Empty — the default — writes no CORS headers at all, which is what a normal install wants: cp-api serves the API and proxies the dashboard, so the browser has only one origin and nothing is ever cross-origin. Populate it only to let a dashboard served from somewhere else reach this API directly, such as a frontend-only PR preview. An entry is a bare origin (https://host[:port]), or `https://*` plus a suffix naming at least three labels (`https://*-api7ai.vercel.app`) — a PR preview's host carries the branch name, so an exact list would mean redeploying cp-api for every pull request. Read the suffix as collision-resistance, NOT as a guarantee: it is a byte suffix, and on a shared host like `.vercel.app` another account can claim a name ending the same way, so a grant that needs a real boundary belongs on a domain you control. Every entry must be spelled the way a BROWSER serializes an origin, and the chart refuses one that is not, naming the spelling to use — a bare `*`, a path, query, fragment or userinfo, a trailing dot, a non-https origin (loopback may use http), an internationalized host given as anything but punycode, a numeric last label that is not a dotted quad, an IPv6 address in any spelling but the one it is sent in, the scheme's default port written out, a port with a leading zero, and a port above 65535. cp-api applies the same rules at startup, so a value this chart accepts is one it will start with. | -| api.deploymentMode | string | `"on-premises"` | How this control plane is deployed: `on-premises` or `saas`. `on-premises` serves one company's organizations: nobody signs up on their own (accounts come from invitations), organizations cannot be created, billing is off, and cp-api's outbound calls may reach private addresses unless api.outbound.ssrfProtection says otherwise. `saas` is the multi-tenant hosted service — open sign-up, organization creation and billing — and requires twoDSN.enabled | +| api.deploymentMode | string | `"on-premises"` | How this control plane is deployed: `on-premises` or `saas`. `on-premises` serves one company's organizations: nobody signs up on their own (accounts come from invitations), organizations cannot be created, billing is off, and cp-api's outbound calls may reach private addresses unless api.outbound.ssrfProtection says otherwise. `saas` is the multi-tenant hosted service — open sign-up, organization creation and billing — and requires twoDSN.enabled, which `on-premises` refuses | | api.dpImage | string | `""` | Gateway image the console hands out in the install snippets it generates for a new data plane. Empty uses cp-api's own default: the gateway of the same release, docker.io/api7/aisix:, or ghcr.io/api7/aisix:dev on a build that is not a release. The Helm snippet takes only its repository; the gateway chart's appVersion picks the tag | | api.dpmgrBaseURL | string | `""` | dp-manager /dp/* mTLS endpoint that data-plane hosts dial, as baked into the generated install snippets. Also passed to the dpm deployment, which seeds this host into the TLS server certificate it presents — so an IP address works here, not only a DNS name. When dpm.service.type is NodePort, use https://:. | | api.extraEnvVars | list | `[]` | Extra environment for cp-api: system-level settings only (for example TZ). Every cp-api setting has its own value in this section; an env var set here for one of them still overrides the rendered config file, which is how values set this way before those keys existed keep working. Overriding a name the chart itself sets is only a compatibility path for existing deployments. For a name chart 1.5.0 already set, the chart keeps its own entry ahead of yours, so the name appears twice and Helm 4's server-side apply rejects the install. New installs set the value in this file instead. | @@ -485,7 +485,7 @@ plane and hands out the gateway image from the same release as the chart. | externalDatabase.sslRootCert.existingSecret | string | `""` | Name of an existing Secret holding the PEM CA bundle that issued the database server's certificate. Mounted read-only into cp-api and dp-manager and used as libpq `sslrootcert` for every database connection they open. Empty leaves verification to the system trust store | | externalDatabase.sslRootCert.key | string | `"ca.crt"` | Key in that Secret holding the CA bundle | | externalDatabase.sslmode | string | `"disable"` | libpq sslmode for the connection, e.g. `require` or `verify-full` for a managed database reached over a network you do not control. `verify-ca` and `verify-full` check the server certificate against the image's system trust store unless `sslRootCert` supplies the CA, which a database whose certificate is issued by a private CA (most managed databases) needs. With `sslRootCert` set, `require` verifies the CA too (libpq semantics) | -| externalDatabase.username | string | `"aisix"` | Database role the control plane connects as. It has to be able to run the schema migrations cp-api applies at startup | +| externalDatabase.username | string | `"aisix"` | Database role the control plane connects as. It has to be able to run the schema migrations cp-api applies at startup: on-premises that means owning the database, with no SUPERUSER, CREATEROLE or BYPASSRLS needed | | global.imagePullSecrets | list | `[]` | Image pull secrets applied to every pod created by this chart | | global.storageClass | string | `""` | StorageClass for the built-in PostgreSQL volume. Empty uses the cluster's default StorageClass | | postgresql.auth.database | string | `"aisix_cloud"` | Database name the control plane uses | @@ -507,7 +507,7 @@ plane and hands out the gateway image from the same release as the chart. | serviceAccount.annotations | object | `{}` | Annotations for the ServiceAccount, e.g. a cloud IAM role binding | | serviceAccount.create | bool | `true` | Create a ServiceAccount for the control-plane pods | | serviceAccount.name | string | `""` | Name of the ServiceAccount. Empty derives one from the release name; set it to use a ServiceAccount you manage yourself (create: false) | -| twoDSN.enabled | bool | `false` | Open a second, Row-Level-Security-enforced pool for tenant queries. Leave off for AISIX Enterprise | +| twoDSN.enabled | bool | `false` | Open a second, Row-Level-Security-enforced pool for tenant queries. Required by api.deploymentMode saas and refused on-premises | | twoDSN.existingSecret | string | `""` | Optionally source the serving password from an existing Secret instead of servingPassword above. Key defaults to "serving-password". | | twoDSN.existingSecretKey | string | `"serving-password"` | Key inside twoDSN.existingSecret holding the serving password | | twoDSN.servingPassword | string | `""` | Password cp-api assigns to the cp_api_app serving role and uses in the serving DSN. Required when enabled unless existingSecret is set. Use a URL-safe value, from `openssl rand -hex 24`: it is embedded in a `postgres://` DSN, so `+`, `/` and `=` corrupt URL parsing | diff --git a/charts/aisix-cp/templates/NOTES.txt b/charts/aisix-cp/templates/NOTES.txt index f642299..338f22b 100644 --- a/charts/aisix-cp/templates/NOTES.txt +++ b/charts/aisix-cp/templates/NOTES.txt @@ -1,4 +1,5 @@ -{{ if eq (.Values.api.deploymentMode | default "on-premises") "saas" }}AISIX Cloud{{ else }}AISIX Enterprise{{ end }} control plane has been deployed. +{{- $mode := include "aisix-cp.effectiveDeploymentMode" . }} +{{- if eq $mode "saas" }}AISIX Cloud{{ else }}AISIX Enterprise{{ end }} control plane has been deployed. Components: - cp-api: {{ include "aisix-cp.fullname" . }}-api:{{ .Values.api.service.port }} (API and dashboard) @@ -37,7 +38,7 @@ cp-api serves the dashboard. Access it through the api Service: {{- end }} {{- $bootstrap := .Values.api.bootstrapAdmin | default dict }} -{{- if and (ne (.Values.api.deploymentMode | default "on-premises") "saas") (or $bootstrap.email .Release.IsInstall) }} +{{- if and (ne $mode "saas") (or $bootstrap.email .Release.IsInstall) }} First administrator: {{- if $bootstrap.email }} diff --git a/charts/aisix-cp/templates/_helpers.tpl b/charts/aisix-cp/templates/_helpers.tpl index a6720c8..935b69a 100644 --- a/charts/aisix-cp/templates/_helpers.tpl +++ b/charts/aisix-cp/templates/_helpers.tpl @@ -302,6 +302,27 @@ Reuses the same PG image shipped by the chart so no extra pull is needed. done {{- end }} +{{/* +The deployment mode cp-api will run in, for choosing what the chart +generates: an AISIX_CLOUD_DEPLOYMENT_MODE entry in api.extraEnvVars +overrides api.deploymentMode, and an empty one means cp-api's default. A +valueFrom entry cannot be read at render time, so api.deploymentMode +stands. Nothing is refused here: cp-api checks the mode at startup. +*/}} +{{- define "aisix-cp.effectiveDeploymentMode" -}} +{{- $mode := .Values.api.deploymentMode | default "on-premises" -}} +{{- range .Values.api.extraEnvVars | default list -}} +{{- if eq .name "AISIX_CLOUD_DEPLOYMENT_MODE" -}} +{{- if .valueFrom -}} +{{- $mode = $.Values.api.deploymentMode | default "on-premises" -}} +{{- else -}} +{{- $mode = trim (toString (.value | default "")) | default "on-premises" -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- $mode -}} +{{- end }} + {{/* Whether the chart's own Secret carries the first administrator's password (key bootstrap-admin-password): the bootstrap is on, it is not read from @@ -311,7 +332,7 @@ bootstrap). */}} {{- define "aisix-cp.bootstrapAdminPasswordManaged" -}} {{- $b := .Values.api.bootstrapAdmin | default dict -}} -{{- if and $b.email (not $b.existingSecret) (or $b.password (ne (.Values.api.deploymentMode | default "on-premises") "saas")) -}} +{{- if and $b.email (not $b.existingSecret) (or $b.password (ne (include "aisix-cp.effectiveDeploymentMode" .) "saas")) -}} true {{- end -}} {{- end }} diff --git a/charts/aisix-cp/templates/api-configmap.yaml b/charts/aisix-cp/templates/api-configmap.yaml index 1307b5f..74aef3c 100644 --- a/charts/aisix-cp/templates/api-configmap.yaml +++ b/charts/aisix-cp/templates/api-configmap.yaml @@ -8,28 +8,11 @@ same setting still overrides this file, which is how a value set through api.extraEnvVars before this file existed keeps its effect. */ -}} {{- include "aisix-cp.validateCorsOrigins" .Values.api.corsAllowedOrigins }} +{{- /* The deployment mode and the settings it requires or forbids are checked +by cp-api at startup, the one place that sees the env overrides too. */ -}} {{- $mode := .Values.api.deploymentMode | default "on-premises" }} -{{- if not (has $mode (list "on-premises" "saas")) }} - {{- fail (printf "api.deploymentMode must be on-premises or saas, got %q" $mode) }} -{{- end }} -{{- if and (eq $mode "saas") (not .Values.twoDSN.enabled) }} - {{- fail "api.deploymentMode saas requires twoDSN.enabled: a SaaS control plane serves tenants through the non-privileged cp_api_app role" }} -{{- end }} -{{- /* A relay set through api.extraEnvVars (the pre-file way) still counts. */ -}} -{{- $smtpHostEnv := false }} -{{- range .Values.api.extraEnvVars }} - {{- if and (eq .name "AISIX_CLOUD_SMTP_HOST") (or .valueFrom (trim (toString (.value | default "")))) }} - {{- $smtpHostEnv = true }} - {{- end }} -{{- end }} -{{- if and (eq $mode "saas") (not (dig "smtp" "host" "" .Values.api | trim)) (not $smtpHostEnv) }} - {{- fail "api.deploymentMode saas requires api.smtp.host: a SaaS control plane emails every new account a verification link and needs an SMTP relay to send it" }} -{{- end }} {{- $billing := .Values.api.billing | default dict }} {{- $billingProvider := $billing.provider | default "" | trim }} -{{- if and (ne $mode "saas") (or $billing.usagePush (and $billingProvider (ne $billingProvider "mock"))) }} - {{- fail "api.billing.provider (other than mock) and api.billing.usagePush need api.deploymentMode saas: an on-premises control plane has no billing" }} -{{- end }} {{- $bootstrap := .Values.api.bootstrapAdmin | default dict }} {{- if and (not $bootstrap.email) (or $bootstrap.password $bootstrap.existingSecret) }} {{- fail "api.bootstrapAdmin.password and api.bootstrapAdmin.existingSecret need api.bootstrapAdmin.email: without it no first administrator is created" }} diff --git a/charts/aisix-cp/values.yaml b/charts/aisix-cp/values.yaml index b98d07f..4bd3f6b 100644 --- a/charts/aisix-cp/values.yaml +++ b/charts/aisix-cp/values.yaml @@ -169,7 +169,7 @@ api: # created, billing is off, and cp-api's outbound calls may reach private # addresses unless api.outbound.ssrfProtection says otherwise. `saas` is the # multi-tenant hosted service — open sign-up, organization creation and - # billing — and requires twoDSN.enabled + # billing — and requires twoDSN.enabled, which `on-premises` refuses deploymentMode: "on-premises" outbound: # SSRF protection for cp-api's own outbound calls to operator- or @@ -258,8 +258,8 @@ api: ## Billing — AISIX Cloud only. AISIX Enterprise (`api.deploymentMode: ## on-premises`) has - ## no billing, and the chart refuses to render with a provider other than - ## `mock` or with usagePush on, as cp-api refuses to start. Every value + ## no billing: cp-api refuses to start with a provider other than `mock` + ## or with usagePush on. Every value ## here is left out of cp-api's config file while it is empty. # @ignored billing: @@ -320,7 +320,7 @@ api: ## hand and asks users who forgot their ## password to contact an administrator. `deploymentMode: saas` requires ## `host`: every new account confirms its address by email before it can - ## sign in, and the chart refuses to render without a relay. + ## sign in, and cp-api refuses to start without a relay. ## Deployments with open public sign-up should keep invitationLimitPerHour ## in place: it is what stops any sign-up from mailing arbitrary addresses ## through the relay. @@ -593,7 +593,8 @@ externalDatabase: # -- PostgreSQL port port: 5432 # -- Database role the control plane connects as. It has to be able to run - # the schema migrations cp-api applies at startup + # the schema migrations cp-api applies at startup: on-premises that means + # owning the database, with no SUPERUSER, CREATEROLE or BYPASSRLS needed username: aisix # -- Database name database: aisix_cloud @@ -629,12 +630,12 @@ externalDatabase: ## cp-api provisions cp_api_app's LOGIN + password (from servingPassword) ## on the admin pool at boot — you do NOT pre-create the role. ## -## Leave DISABLED for AISIX Enterprise deployments: there -## every organization is one trust domain, RLS is inert, and the single -## privileged DSN handles everything. +## Only for api.deploymentMode saas, which requires it. An AISIX Enterprise +## (on-premises) control plane refuses it: it uses no Row-Level Security, and +## its one DSN handles everything with a role that owns the database. twoDSN: # -- Open a second, Row-Level-Security-enforced pool for tenant queries. - # Leave off for AISIX Enterprise + # Required by api.deploymentMode saas and refused on-premises enabled: false # -- Password cp-api assigns to the cp_api_app serving role and uses in the # serving DSN. Required when enabled unless existingSecret is set. diff --git a/charts/aisix/Chart.yaml b/charts/aisix/Chart.yaml index 28f60ce..47572c7 100644 --- a/charts/aisix/Chart.yaml +++ b/charts/aisix/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: aisix description: Helm chart for the AISIX AI gateway data plane type: application -version: 1.7.0 -appVersion: "1.7.0" +version: 1.7.1 +appVersion: "1.7.1" keywords: - ai-gateway diff --git a/charts/aisix/README.md b/charts/aisix/README.md index 0ef23ec..9358ac9 100644 --- a/charts/aisix/README.md +++ b/charts/aisix/README.md @@ -1,6 +1,6 @@ # aisix -![Version: 1.7.0](https://img.shields.io/badge/Version-1.7.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.0](https://img.shields.io/badge/AppVersion-1.7.0-informational?style=flat-square) +![Version: 1.7.1](https://img.shields.io/badge/Version-1.7.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.1](https://img.shields.io/badge/AppVersion-1.7.1-informational?style=flat-square) Helm chart for the AISIX AI gateway data plane